Commit 24eb4fc0 authored by tom goriunov's avatar tom goriunov Committed by GitHub

API resource refactoring (#2699)

* add prefix to API resources

* fix pw tests

* refactor config

* remove unnecessary prefixes in resource names

* fixes

* update screenshots
parent b54be82e
import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue } from './utils';
const apiHost = getEnvValue('NEXT_PUBLIC_API_HOST');
const apiSchema = getEnvValue('NEXT_PUBLIC_API_PROTOCOL') || 'https';
const apiPort = getEnvValue('NEXT_PUBLIC_API_PORT');
const apiEndpoint = [
apiSchema || 'https',
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('');
const socketSchema = getEnvValue('NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL') || 'wss';
const socketEndpoint = [
socketSchema,
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('');
const api = Object.freeze({
host: apiHost,
protocol: apiSchema,
port: apiPort,
endpoint: apiEndpoint,
socket: socketEndpoint,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''),
});
export default api;
import type { ApiName } from 'lib/api/types';
import { stripTrailingSlash } from 'toolkit/utils/url';
import { getEnvValue } from './utils';
interface ApiPropsBase {
endpoint: string;
basePath?: string;
}
interface ApiPropsFull extends ApiPropsBase {
host: string;
protocol: string;
port?: string;
socketEndpoint: string;
}
const generalApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_API_HOST');
const apiSchema = getEnvValue('NEXT_PUBLIC_API_PROTOCOL') || 'https';
const apiPort = getEnvValue('NEXT_PUBLIC_API_PORT');
const apiEndpoint = [
apiSchema || 'https',
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('');
const socketSchema = getEnvValue('NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL') || 'wss';
const socketEndpoint = [
socketSchema,
'://',
apiHost,
apiPort && ':' + apiPort,
].filter(Boolean).join('');
return Object.freeze({
endpoint: apiEndpoint,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_API_BASE_PATH') || ''),
socketEndpoint: socketEndpoint,
host: apiHost ?? '',
protocol: apiSchema ?? 'https',
port: apiPort,
});
})();
const adminApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
});
})();
const bensApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_NAME_SERVICE_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
});
})();
const contractInfoApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
});
})();
const metadataApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_METADATA_SERVICE_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
});
})();
const rewardsApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_REWARDS_SERVICE_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
});
})();
const statsApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_STATS_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_STATS_API_BASE_PATH') || ''),
});
})();
const visualizeApi = (() => {
const apiHost = getEnvValue('NEXT_PUBLIC_VISUALIZE_API_HOST');
if (!apiHost) {
return;
}
return Object.freeze({
endpoint: apiHost,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_VISUALIZE_API_BASE_PATH') || ''),
});
})();
type Apis = {
general: ApiPropsFull;
} & Partial<Record<Exclude<ApiName, 'general'>, ApiPropsBase>>;
const apis: Apis = Object.freeze({
general: generalApi,
admin: adminApi,
bens: bensApi,
contractInfo: contractInfoApi,
metadata: metadataApi,
rewards: rewardsApi,
stats: statsApi,
visualize: visualizeApi,
});
export default apis;
import type { Feature } from './types'; import type { Feature } from './types';
import { getEnvValue } from '../utils'; import apis from '../apis';
const apiHost = getEnvValue('NEXT_PUBLIC_METADATA_SERVICE_API_HOST');
const title = 'Address metadata'; const title = 'Address metadata';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (apiHost) { if (apis.metadata) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { getEnvValue } from '../utils'; import apis from '../apis';
import account from './account'; import account from './account';
import verifiedTokens from './verifiedTokens'; import verifiedTokens from './verifiedTokens';
const adminServiceApiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST');
const title = 'Address verification in "My account"'; const title = 'Address verification in "My account"';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (account.isEnabled && verifiedTokens.isEnabled && adminServiceApiHost) { if (account.isEnabled && verifiedTokens.isEnabled && apis.admin) {
return Object.freeze({ return Object.freeze({
title: 'Address verification in "My account"', title: 'Address verification in "My account"',
isEnabled: true, isEnabled: true,
api: {
endpoint: adminServiceApiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import apis from '../apis';
import chain from '../chain'; import chain from '../chain';
import { getEnvValue, getExternalAssetFilePath } from '../utils'; import { getEnvValue, getExternalAssetFilePath } from '../utils';
...@@ -9,7 +10,6 @@ const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL') ...@@ -9,7 +10,6 @@ const configUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CONFIG_URL')
const submitFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM'); const submitFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM');
const suggestIdeasFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM'); const suggestIdeasFormUrl = getEnvValue('NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM');
const categoriesUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL'); const categoriesUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL');
const adminServiceApiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST');
const securityReportsUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL'); const securityReportsUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL');
const featuredApp = getEnvValue('NEXT_PUBLIC_MARKETPLACE_FEATURED_APP'); const featuredApp = getEnvValue('NEXT_PUBLIC_MARKETPLACE_FEATURED_APP');
const bannerContentUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL'); const bannerContentUrl = getExternalAssetFilePath('NEXT_PUBLIC_MARKETPLACE_BANNER_CONTENT_URL');
...@@ -22,7 +22,7 @@ const title = 'Marketplace'; ...@@ -22,7 +22,7 @@ const title = 'Marketplace';
const config: Feature<( const config: Feature<(
{ configUrl: string } | { configUrl: string } |
{ api: { endpoint: string; basePath: string } } { api: { endpoint: string; basePath?: string } }
) & { ) & {
submitFormUrl: string; submitFormUrl: string;
categoriesUrl: string | undefined; categoriesUrl: string | undefined;
...@@ -58,14 +58,11 @@ const config: Feature<( ...@@ -58,14 +58,11 @@ const config: Feature<(
configUrl, configUrl,
...props, ...props,
}); });
} else if (adminServiceApiHost) { } else if (apis.admin) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: { api: apis.admin,
endpoint: adminServiceApiHost,
basePath: '',
},
...props, ...props,
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { getEnvValue } from '../utils'; import apis from '../apis';
const apiHost = getEnvValue('NEXT_PUBLIC_NAME_SERVICE_API_HOST');
const title = 'Name service integration'; const title = 'Name service integration';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (apiHost) { if (apis.bens) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import apis from '../apis';
import { getEnvValue } from '../utils'; import { getEnvValue } from '../utils';
const contractInfoApiHost = getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_API_HOST');
const dexPoolsEnabled = getEnvValue('NEXT_PUBLIC_DEX_POOLS_ENABLED') === 'true'; const dexPoolsEnabled = getEnvValue('NEXT_PUBLIC_DEX_POOLS_ENABLED') === 'true';
const title = 'DEX Pools'; const title = 'DEX Pools';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{ }> = (() => {
if (contractInfoApiHost && dexPoolsEnabled) { if (apis.contractInfo && dexPoolsEnabled) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: contractInfoApiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import apis from '../apis';
import services from '../services'; import services from '../services';
import { getEnvValue } from '../utils';
import addressMetadata from './addressMetadata'; import addressMetadata from './addressMetadata';
const apiHost = getEnvValue('NEXT_PUBLIC_ADMIN_SERVICE_API_HOST');
const title = 'Public tag submission'; const title = 'Public tag submission';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (services.reCaptchaV2.siteKey && addressMetadata.isEnabled && apiHost) { if (services.reCaptchaV2.siteKey && addressMetadata.isEnabled && apis.admin) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { getEnvValue } from '../utils'; import apis from '../apis';
import account from './account'; import account from './account';
import blockchainInteraction from './blockchainInteraction'; import blockchainInteraction from './blockchainInteraction';
const apiHost = getEnvValue('NEXT_PUBLIC_REWARDS_SERVICE_API_HOST');
const title = 'Rewards service integration'; const title = 'Rewards service integration';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (apiHost && account.isEnabled && blockchainInteraction.isEnabled) { if (apis.rewards && account.isEnabled && blockchainInteraction.isEnabled) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiHost,
basePath: '',
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { stripTrailingSlash } from 'toolkit/utils/url'; import apis from '../apis';
import { getEnvValue } from '../utils';
const apiEndpoint = getEnvValue('NEXT_PUBLIC_VISUALIZE_API_HOST');
const title = 'Solidity to UML diagrams'; const title = 'Solidity to UML diagrams';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (apiEndpoint) { if (apis.visualize) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiEndpoint,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_VISUALIZE_API_BASE_PATH') || ''),
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { stripTrailingSlash } from 'toolkit/utils/url'; import apis from '../apis';
import { getEnvValue } from '../utils';
const apiEndpoint = getEnvValue('NEXT_PUBLIC_STATS_API_HOST');
const title = 'Blockchain statistics'; const title = 'Blockchain statistics';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (apiEndpoint) { if (apis.stats) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: apiEndpoint,
basePath: stripTrailingSlash(getEnvValue('NEXT_PUBLIC_STATS_API_BASE_PATH') || ''),
},
}); });
} }
......
import type { Feature } from './types'; import type { Feature } from './types';
import { getEnvValue } from '../utils'; import apis from '../apis';
const contractInfoApiHost = getEnvValue('NEXT_PUBLIC_CONTRACT_INFO_API_HOST');
const title = 'Verified tokens info'; const title = 'Verified tokens info';
const config: Feature<{ api: { endpoint: string; basePath: string } }> = (() => { const config: Feature<{}> = (() => {
if (contractInfoApiHost) { if (apis.contractInfo) {
return Object.freeze({ return Object.freeze({
title, title,
isEnabled: true, isEnabled: true,
api: {
endpoint: contractInfoApiHost,
basePath: '',
},
}); });
} }
......
import api from './api'; import apis from './apis';
import app from './app'; import app from './app';
import chain from './chain'; import chain from './chain';
import * as features from './features'; import * as features from './features';
...@@ -9,7 +9,7 @@ import UI from './ui'; ...@@ -9,7 +9,7 @@ import UI from './ui';
const config = Object.freeze({ const config = Object.freeze({
app, app,
chain, chain,
api, apis,
UI, UI,
features, features,
services, services,
......
...@@ -7,15 +7,9 @@ NEXT_PUBLIC_APP_PROTOCOL=http ...@@ -7,15 +7,9 @@ NEXT_PUBLIC_APP_PROTOCOL=http
NEXT_PUBLIC_APP_HOST=localhost NEXT_PUBLIC_APP_HOST=localhost
NEXT_PUBLIC_APP_PORT=3000 NEXT_PUBLIC_APP_PORT=3000
NEXT_PUBLIC_APP_ENV=development NEXT_PUBLIC_APP_ENV=development
NEXT_PUBLIC_APP_INSTANCE=rubber_duck
NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
# Instance ENVs # Instance ENVs
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_DESKTOP={ "id": "632019", "width": "728", "height": "90" }
NEXT_PUBLIC_AD_ADBUTLER_CONFIG_MOBILE={ "id": "632018", "width": "320", "height": "100" }
NEXT_PUBLIC_AD_BANNER_ADDITIONAL_PROVIDER=adbutler
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=https://admin-rs-test.k8s-dev.blockscout.com
NEXT_PUBLIC_API_BASE_PATH=/ NEXT_PUBLIC_API_BASE_PATH=/
NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_API_HOST=eth-sepolia.k8s-dev.blockscout.com
...@@ -23,28 +17,33 @@ NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout ...@@ -23,28 +17,33 @@ 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_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-test.k8s-dev.blockscout.com
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED=true
NEXT_PUBLIC_DEFI_DROPDOWN_ITEMS=[{'text':'Swap','icon':'swap','dappId':'uniswap'},{'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-sepolia.json 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_FOOTER_LINKS=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/footer-links/sepolia.json
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0xbf69c7abc4fee283b59a9633dadfdaedde5c5ee0fba3e80a08b5b8a3acbd4363 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}', 'dapp_id': 'smol-refuel', 'logo': 'https://blockscout-content.s3.amazonaws.com/smolrefuel-logo-action-button.png'}
NEXT_PUBLIC_GRAPHIQL_TRANSACTION=0x93e00d4d48cf0dc229f5102e18277fa1bb6130d5b319697a87698a35cf67f706
NEXT_PUBLIC_HAS_BEACON_CHAIN=true NEXT_PUBLIC_HAS_BEACON_CHAIN=true
NEXT_PUBLIC_HAS_USER_OPS=true NEXT_PUBLIC_HAS_USER_OPS=true
NEXT_PUBLIC_HELIA_VERIFIED_FETCH_ENABLED=false
NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs']
NEXT_PUBLIC_HOMEPAGE_PLATE_BACKGROUND=rgba(51, 53, 67, 1) NEXT_PUBLIC_HOMEPAGE_HERO_BANNER_CONFIG={'background':['rgba(51, 53, 67, 1)'],'text_color':['rgba(165, 252, 122, 1)']}
NEXT_PUBLIC_HOMEPAGE_PLATE_TEXT_COLOR=rgba(165, 252, 122, 1)
NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true NEXT_PUBLIC_IS_ACCOUNT_SUPPORTED=true
NEXT_PUBLIC_IS_TESTNET=true NEXT_PUBLIC_IS_TESTNET=true
NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout NEXT_PUBLIC_LOGOUT_URL=https://blockscout-goerli.us.auth0.com/v2/logout
NEXT_PUBLIC_MARKETPLACE_CATEGORIES_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-categories/default.json 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_ENABLED=true
NEXT_PUBLIC_MARKETPLACE_GRAPH_LINKS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/refs/heads/marketplace-graph-test/test-configs/marketplace-graph-links.json
NEXT_PUBLIC_MARKETPLACE_RATING_AIRTABLE_BASE_ID=appGkvtmKI7fXE4Vs
NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/test-configs/marketplace-security-report-mock.json NEXT_PUBLIC_MARKETPLACE_SECURITY_REPORTS_URL=https://raw.githubusercontent.com/blockscout/frontend-configs/main/test-configs/marketplace-security-report-mock.json
NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL NEXT_PUBLIC_MARKETPLACE_SUBMIT_FORM=https://airtable.com/appiy5yijZpMMSKjT/shr6uMGPKjj1DK7NL
NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form NEXT_PUBLIC_MARKETPLACE_SUGGEST_IDEAS_FORM=https://airtable.com/appiy5yijZpMMSKjT/pag3t82DUCyhGRZZO/form
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata-test.k8s-dev.blockscout.com NEXT_PUBLIC_METADATA_SERVICE_API_HOST=https://metadata-test.k8s-dev.blockscout.com
NEXT_PUBLIC_METASUITES_ENABLED=true 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'}] NEXT_PUBLIC_MULTICHAIN_BALANCE_PROVIDER_CONFIG=[{'name': 'zerion', 'url_template': 'https://app.zerion.io/{address}/overview?utm_source=blockscout&utm_medium=address', 'logo': 'https://raw.githubusercontent.com/blockscout/frontend-configs/main/configs/marketplace-logos/zerion.svg'},{'name': 'zapper', 'url_template': 'https://zapper.xyz/account/{address}?utm_source=blockscout&utm_medium=address', 'logo': 'https://blockscout-content.s3.amazonaws.com/zapper-icon.png'}]
NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens-rs-test.k8s-dev.blockscout.com NEXT_PUBLIC_NAME_SERVICE_API_HOST=https://bens-rs-test.k8s-dev.blockscout.com
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/blocks','/apps'] NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES=['/pools']
NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal NEXT_PUBLIC_NAVIGATION_LAYOUT=horizontal
NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18
NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether
...@@ -64,8 +63,13 @@ NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-c ...@@ -64,8 +63,13 @@ NEXT_PUBLIC_OG_IMAGE_URL=https://raw.githubusercontent.com/blockscout/frontend-c
NEXT_PUBLIC_OTHER_LINKS=[{'url':'https://sepolia.drpc.org?ref=559183','text':'Public RPC'}] 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://points.k8s-dev.blockscout.com
NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global NEXT_PUBLIC_SAFE_TX_SERVICE_URL=https://safe-transaction-sepolia.safe.global
NEXT_PUBLIC_SAVE_ON_GAS_ENABLED=true
NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true NEXT_PUBLIC_SEO_ENHANCED_DATA_ENABLED=true
NEXT_PUBLIC_STATS_API_HOST=https://stats-sepolia.k8s-dev.blockscout.com NEXT_PUBLIC_STATS_API_BASE_PATH=/stats-service
NEXT_PUBLIC_STATS_API_HOST=https://eth-sepolia.k8s-dev.blockscout.com
NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout NEXT_PUBLIC_TRANSACTION_INTERPRETATION_PROVIDER=blockscout
NEXT_PUBLIC_VIEWS_ADDRESS_IDENTICON_TYPE=nouns
NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true NEXT_PUBLIC_VIEWS_CONTRACT_SOLIDITYSCAN_ENABLED=true
NEXT_PUBLIC_VIEWS_TOKEN_SCAM_TOGGLE_ENABLED=true
NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer-test.k8s-dev.blockscout.com NEXT_PUBLIC_VISUALIZE_API_HOST=https://visualizer-test.k8s-dev.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
...@@ -52,6 +52,7 @@ NEXT_PUBLIC_CONTRACT_INFO_API_HOST=http://localhost:3005 ...@@ -52,6 +52,7 @@ NEXT_PUBLIC_CONTRACT_INFO_API_HOST=http://localhost:3005
NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=http://localhost:3006 NEXT_PUBLIC_ADMIN_SERVICE_API_HOST=http://localhost:3006
NEXT_PUBLIC_METADATA_SERVICE_API_HOST=http://localhost:3007 NEXT_PUBLIC_METADATA_SERVICE_API_HOST=http://localhost:3007
NEXT_PUBLIC_NAME_SERVICE_API_HOST=http://localhost:3008 NEXT_PUBLIC_NAME_SERVICE_API_HOST=http://localhost:3008
NEXT_PUBLIC_REWARDS_SERVICE_API_HOST=http://localhost:3009
NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx NEXT_PUBLIC_RE_CAPTCHA_APP_SITE_KEY=xxx
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=xxx
NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32'] NEXT_PUBLIC_VIEWS_ADDRESS_FORMAT=['base16','bech32']
......
...@@ -7,7 +7,7 @@ import parseMetaPayload from './parseMetaPayload'; ...@@ -7,7 +7,7 @@ import parseMetaPayload from './parseMetaPayload';
export default function useAddressMetadataInfoQuery(addresses: Array<string>, isEnabled = true) { export default function useAddressMetadataInfoQuery(addresses: Array<string>, isEnabled = true) {
const resource = 'address_metadata_info'; const resource = 'metadata:info';
return useApiQuery<typeof resource, unknown, AddressMetadataInfoFormatted>(resource, { return useApiQuery<typeof resource, unknown, AddressMetadataInfoFormatted>(resource, {
queryParams: { queryParams: {
......
import buildUrl from './buildUrl'; import buildUrl from './buildUrl';
test('builds URL for resource without path params', () => { test('builds URL for resource without path params', () => {
const url = buildUrl('config_backend_version'); const url = buildUrl('general:config_backend_version');
expect(url).toBe('https://localhost:3003/api/v2/config/backend-version'); expect(url).toBe('https://localhost:3003/api/v2/config/backend-version');
}); });
test('builds URL for resource with path params', () => { test('builds URL for resource with path params', () => {
const url = buildUrl('block', { height_or_hash: '42' }); const url = buildUrl('general:block', { height_or_hash: '42' });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42');
}); });
describe('falsy query parameters', () => { describe('falsy query parameters', () => {
test('leaves "false" as query parameter', () => { test('leaves "false" as query parameter', () => {
const url = buildUrl('block', { height_or_hash: '42' }, { includeTx: false }); const url = buildUrl('general:block', { height_or_hash: '42' }, { includeTx: false });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=false'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=false');
}); });
test('leaves "null" as query parameter', () => { test('leaves "null" as query parameter', () => {
const url = buildUrl('block', { height_or_hash: '42' }, { includeTx: null }); const url = buildUrl('general:block', { height_or_hash: '42' }, { includeTx: null });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null');
}); });
test('strips out empty string as query parameter', () => { test('strips out empty string as query parameter', () => {
const url = buildUrl('block', { height_or_hash: '42' }, { includeTx: null, sort: '' }); const url = buildUrl('general:block', { height_or_hash: '42' }, { includeTx: null, sort: '' });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null');
}); });
test('strips out "undefined" as query parameter', () => { test('strips out "undefined" as query parameter', () => {
const url = buildUrl('block', { height_or_hash: '42' }, { includeTx: null, sort: undefined }); const url = buildUrl('general:block', { height_or_hash: '42' }, { includeTx: null, sort: undefined });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=null');
}); });
}); });
test('builds URL with array-like query parameters', () => { test('builds URL with array-like query parameters', () => {
const url = buildUrl('block', { height_or_hash: '42' }, { includeTx: [ '0x11', '0x22' ], sort: 'asc' }); const url = buildUrl('general:block', { height_or_hash: '42' }, { includeTx: [ '0x11', '0x22' ], sort: 'asc' });
expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=0x11%2C0x22&sort=asc'); expect(url).toBe('https://localhost:3003/api/v2/blocks/42?includeTx=0x11%2C0x22&sort=asc');
}); });
test('builds URL for resource with custom API endpoint', () => { test('builds URL for resource with custom API endpoint', () => {
const url = buildUrl('token_verified_info', { chainId: '42', hash: '0x11' }); const url = buildUrl('contractInfo:token_verified_info', { chainId: '42', hash: '0x11' });
expect(url).toBe('https://localhost:3005/api/v1/chains/42/token-infos/0x11'); expect(url).toBe('https://localhost:3005/api/v1/chains/42/token-infos/0x11');
}); });
...@@ -2,19 +2,19 @@ import { compile } from 'path-to-regexp'; ...@@ -2,19 +2,19 @@ import { compile } from 'path-to-regexp';
import config from 'configs/app'; import config from 'configs/app';
import getResourceParams from './getResourceParams';
import isNeedProxy from './isNeedProxy'; import isNeedProxy from './isNeedProxy';
import { RESOURCES } from './resources'; import type { ResourceName, ResourcePathParams } from './resources';
import type { ApiResource, ResourceName, ResourcePathParams } from './resources';
export default function buildUrl<R extends ResourceName>( export default function buildUrl<R extends ResourceName>(
resourceName: R, resourceFullName: R,
pathParams?: ResourcePathParams<R>, pathParams?: ResourcePathParams<R>,
queryParams?: Record<string, string | Array<string> | number | boolean | null | undefined>, queryParams?: Record<string, string | Array<string> | number | boolean | null | undefined>,
noProxy?: boolean, noProxy?: boolean,
): string { ): string {
const resource: ApiResource = RESOURCES[resourceName]; const { api, resource } = getResourceParams(resourceFullName);
const baseUrl = !noProxy && isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint); const baseUrl = !noProxy && isNeedProxy() ? config.app.baseUrl : api.endpoint;
const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath; const basePath = api.basePath ?? '';
const path = !noProxy && isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path; const path = !noProxy && isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl); const url = new URL(compile(path)(pathParams), baseUrl);
......
import type { ApiName, ApiResource } from './types';
import config from 'configs/app';
import type { ResourceName } from './resources';
import { RESOURCES } from './resources';
export default function getResourceParams(resourceFullName: ResourceName) {
const [ apiName, resourceName ] = resourceFullName.split(':') as [ ApiName, string ];
const apiConfig = config.apis[apiName];
if (!apiConfig) {
throw new Error(`API config for ${ apiName } not found`);
}
return {
api: apiConfig,
apiName,
resource: RESOURCES[apiName][resourceName as keyof typeof RESOURCES[ApiName]] as ApiResource,
};
}
...@@ -9,5 +9,5 @@ export default function isNeedProxy() { ...@@ -9,5 +9,5 @@ export default function isNeedProxy() {
return true; return true;
} }
return config.app.host === 'localhost' && config.app.host !== config.api.host; return config.app.host === 'localhost' && config.app.host !== config.apis.general.host;
} }
This diff is collapsed.
import type { ApiResource } from '../types';
import type { TokenInfoApplicationConfig, TokenInfoApplications } from 'types/api/account';
import type { MarketplaceAppOverview } from 'types/client/marketplace';
export const ADMIN_API_RESOURCES = {
public_tag_application: {
path: '/api/v1/chains/:chainId/metadata-submissions/tag',
pathParams: [ 'chainId' as const ],
},
token_info_applications_config: {
path: '/api/v1/chains/:chainId/token-info-submissions/selectors',
pathParams: [ 'chainId' as const ],
},
token_info_applications: {
path: '/api/v1/chains/:chainId/token-info-submissions{/:id}',
pathParams: [ 'chainId' as const, 'id' as const ],
},
marketplace_dapps: {
path: '/api/v1/chains/:chainId/marketplace/dapps',
pathParams: [ 'chainId' as const ],
},
marketplace_dapp: {
path: '/api/v1/chains/:chainId/marketplace/dapps/:dappId',
pathParams: [ 'chainId' as const, 'dappId' as const ],
},
} satisfies Record<string, ApiResource>;
export type AdminApiResourceName = `admin:${ keyof typeof ADMIN_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type AdminApiResourcePayload<R extends AdminApiResourceName> =
R extends 'admin:token_info_applications_config' ? TokenInfoApplicationConfig :
R extends 'admin:token_info_applications' ? TokenInfoApplications :
R extends 'admin:marketplace_dapps' ? Array<MarketplaceAppOverview> :
R extends 'admin:marketplace_dapp' ? MarketplaceAppOverview :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
import type * as bens from '@blockscout/bens-types';
import type { EnsAddressLookupFilters, EnsDomainLookupFilters, EnsLookupSorting } from 'types/api/ens';
export const BENS_API_RESOURCES = {
addresses_lookup: {
path: '/api/v1/:chainId/addresses\\:lookup',
pathParams: [ 'chainId' as const ],
filterFields: [ 'address' as const, 'resolved_to' as const, 'owned_by' as const, 'only_active' as const, 'protocols' as const ],
paginated: true,
},
address_domain: {
path: '/api/v1/:chainId/addresses/:address',
pathParams: [ 'chainId' as const, 'address' as const ],
},
domain_info: {
path: '/api/v1/:chainId/domains/:name',
pathParams: [ 'chainId' as const, 'name' as const ],
},
domain_events: {
path: '/api/v1/:chainId/domains/:name/events',
pathParams: [ 'chainId' as const, 'name' as const ],
},
domains_lookup: {
path: '/api/v1/:chainId/domains\\:lookup',
pathParams: [ 'chainId' as const ],
filterFields: [ 'name' as const, 'only_active' as const, 'protocols' as const ],
paginated: true,
},
domain_protocols: {
path: '/api/v1/:chainId/protocols',
pathParams: [ 'chainId' as const ],
},
} satisfies Record<string, ApiResource>;
export type BensApiResourceName = `bens:${ keyof typeof BENS_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type BensApiResourcePayload<R extends BensApiResourceName> =
R extends 'bens:addresses_lookup' ? bens.LookupAddressResponse :
R extends 'bens:address_domain' ? bens.GetAddressResponse :
R extends 'bens:domain_info' ? bens.DetailedDomain :
R extends 'bens:domain_events' ? bens.ListDomainEventsResponse :
R extends 'bens:domains_lookup' ? bens.LookupDomainNameResponse :
R extends 'bens:domain_protocols' ? bens.GetProtocolsResponse :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type BensApiPaginationFilters<R extends BensApiResourceName> =
R extends 'bens:addresses_lookup' ? EnsAddressLookupFilters :
R extends 'bens:domains_lookup' ? EnsDomainLookupFilters :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type BensApiPaginationSorting<R extends BensApiResourceName> =
R extends 'bens:addresses_lookup' ? EnsLookupSorting :
R extends 'bens:domains_lookup' ? EnsLookupSorting :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
import type { VerifiedAddressResponse } from 'types/api/account';
import type { Pool, PoolsResponse } from 'types/api/pools';
import type { TokenVerifiedInfo } from 'types/api/token';
export const CONTRACT_INFO_API_RESOURCES = {
address_verification: {
path: '/api/v1/chains/:chainId/verified-addresses:type',
pathParams: [ 'chainId' as const, 'type' as const ],
},
verified_addresses: {
path: '/api/v1/chains/:chainId/verified-addresses',
pathParams: [ 'chainId' as const ],
},
token_verified_info: {
path: '/api/v1/chains/:chainId/token-infos/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ],
},
pools: {
path: '/api/v1/chains/:chainId/pools',
pathParams: [ 'chainId' as const ],
filterFields: [ 'query' as const ],
paginated: true,
},
pool: {
path: '/api/v1/chains/:chainId/pools/:hash',
pathParams: [ 'chainId' as const, 'hash' as const ],
},
} satisfies Record<string, ApiResource>;
export type ContractInfoApiResourceName = `contractInfo:${ keyof typeof CONTRACT_INFO_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type ContractInfoApiResourcePayload<R extends ContractInfoApiResourceName> =
R extends 'contractInfo:verified_addresses' ? VerifiedAddressResponse :
R extends 'contractInfo:token_verified_info' ? TokenVerifiedInfo :
R extends 'contractInfo:pools' ? PoolsResponse :
R extends 'contractInfo:pool' ? Pool :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type ContractInfoApiPaginationFilters<R extends ContractInfoApiResourceName> =
R extends 'contractInfo:pools' ? { query: string } :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type { AddressTagsResponse, ApiKeys, CustomAbis, TransactionTagsResponse, UserInfo, WatchlistResponse } from 'types/api/account';
export const GENERAL_API_ACCOUNT_RESOURCES = {
// ACCOUNT
csrf: {
path: '/api/account/v2/get_csrf',
},
user_info: {
path: '/api/account/v2/user/info',
},
custom_abi: {
path: '/api/account/v2/user/custom_abis{/:id}',
pathParams: [ 'id' as const ],
},
watchlist: {
path: '/api/account/v2/user/watchlist{/:id}',
pathParams: [ 'id' as const ],
filterFields: [ ],
paginated: true,
},
private_tags_address: {
path: '/api/account/v2/user/tags/address{/:id}',
pathParams: [ 'id' as const ],
filterFields: [ ],
paginated: true,
},
private_tags_tx: {
path: '/api/account/v2/user/tags/transaction{/:id}',
pathParams: [ 'id' as const ],
filterFields: [ ],
paginated: true,
},
api_keys: {
path: '/api/account/v2/user/api_keys{/:id}',
pathParams: [ 'id' as const ],
},
// AUTH
auth_send_otp: {
path: '/api/account/v2/send_otp',
},
auth_confirm_otp: {
path: '/api/account/v2/confirm_otp',
},
auth_siwe_message: {
path: '/api/account/v2/siwe_message',
},
auth_siwe_verify: {
path: '/api/account/v2/authenticate_via_wallet',
},
auth_link_email: {
path: '/api/account/v2/email/link',
},
auth_link_address: {
path: '/api/account/v2/address/link',
},
auth_logout: {
path: '/api/account/auth/logout',
},
} satisfies Record<string, ApiResource>;
export type GeneralApiAccountResourceName = `general:${ keyof typeof GENERAL_API_ACCOUNT_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiAccountResourcePayload<R extends GeneralApiAccountResourceName> =
R extends 'general:user_info' ? UserInfo :
R extends 'general:custom_abi' ? CustomAbis :
R extends 'general:private_tags_address' ? AddressTagsResponse :
R extends 'general:private_tags_tx' ? TransactionTagsResponse :
R extends 'general:api_keys' ? ApiKeys :
R extends 'general:watchlist' ? WatchlistResponse :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type {
AddressCounters,
AddressBlocksValidatedResponse,
AddressTokensResponse,
AddressCollectionsResponse,
AddressEpochRewardsResponse,
AddressNFTsResponse,
AddressWithdrawalsResponse,
AddressXStarResponse,
AddressCoinBalanceHistoryChart,
AddressCoinBalanceHistoryResponse,
AddressTokenTransferResponse,
AddressInternalTxsResponse,
AddressTransactionsResponse,
AddressTabsCounters,
Address,
AddressTxsFilters,
AddressTokenTransferFilters,
AddressTokensFilter,
AddressNFTTokensFilter,
} from 'types/api/address';
import type { AddressesMetadataSearchFilters, AddressesMetadataSearchResult, AddressesResponse } from 'types/api/addresses';
import type { LogsResponseAddress } from 'types/api/log';
import type { TransactionsSorting } from 'types/api/transaction';
export const GENERAL_API_ADDRESS_RESOURCES = {
// ADDRESSES
addresses: {
path: '/api/v2/addresses/',
filterFields: [ ],
paginated: true,
},
addresses_metadata_search: {
path: '/api/v2/proxy/metadata/addresses',
filterFields: [ 'slug' as const, 'tag_type' as const ],
paginated: true,
},
// ADDRESS INFO
address: {
path: '/api/v2/addresses/:hash',
pathParams: [ 'hash' as const ],
},
address_counters: {
path: '/api/v2/addresses/:hash/counters',
pathParams: [ 'hash' as const ],
},
address_tabs_counters: {
path: '/api/v2/addresses/:hash/tabs-counters',
pathParams: [ 'hash' as const ],
},
address_txs: {
path: '/api/v2/addresses/:hash/transactions',
pathParams: [ 'hash' as const ],
filterFields: [ 'filter' as const ],
paginated: true,
},
address_internal_txs: {
path: '/api/v2/addresses/:hash/internal-transactions',
pathParams: [ 'hash' as const ],
filterFields: [ 'filter' as const ],
paginated: true,
},
address_token_transfers: {
path: '/api/v2/addresses/:hash/token-transfers',
pathParams: [ 'hash' as const ],
filterFields: [ 'filter' as const, 'type' as const, 'token' as const ],
paginated: true,
},
address_blocks_validated: {
path: '/api/v2/addresses/:hash/blocks-validated',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
address_coin_balance: {
path: '/api/v2/addresses/:hash/coin-balance-history',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
address_coin_balance_chart: {
path: '/api/v2/addresses/:hash/coin-balance-history-by-day',
pathParams: [ 'hash' as const ],
filterFields: [ ],
},
address_logs: {
path: '/api/v2/addresses/:hash/logs',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
address_tokens: {
path: '/api/v2/addresses/:hash/tokens',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
paginated: true,
},
address_nfts: {
path: '/api/v2/addresses/:hash/nft',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
paginated: true,
},
address_collections: {
path: '/api/v2/addresses/:hash/nft/collections',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
paginated: true,
},
address_withdrawals: {
path: '/api/v2/addresses/:hash/withdrawals',
pathParams: [ 'hash' as const ],
filterFields: [],
paginated: true,
},
address_epoch_rewards: {
path: '/api/v2/addresses/:hash/election-rewards',
pathParams: [ 'hash' as const ],
filterFields: [],
paginated: true,
},
address_xstar_score: {
path: '/api/v2/proxy/3dparty/xname/addresses/:hash',
pathParams: [ 'hash' as const ],
},
} satisfies Record<string, ApiResource>;
export type GeneralApiAddressResourceName = `general:${ keyof typeof GENERAL_API_ADDRESS_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiAddressResourcePayload<R extends GeneralApiAddressResourceName> =
R extends 'general:addresses' ? AddressesResponse :
R extends 'general:addresses_metadata_search' ? AddressesMetadataSearchResult :
R extends 'general:address' ? Address :
R extends 'general:address_counters' ? AddressCounters :
R extends 'general:address_tabs_counters' ? AddressTabsCounters :
R extends 'general:address_txs' ? AddressTransactionsResponse :
R extends 'general:address_internal_txs' ? AddressInternalTxsResponse :
R extends 'general:address_token_transfers' ? AddressTokenTransferResponse :
R extends 'general:address_blocks_validated' ? AddressBlocksValidatedResponse :
R extends 'general:address_coin_balance' ? AddressCoinBalanceHistoryResponse :
R extends 'general:address_coin_balance_chart' ? AddressCoinBalanceHistoryChart :
R extends 'general:address_logs' ? LogsResponseAddress :
R extends 'general:address_tokens' ? AddressTokensResponse :
R extends 'general:address_nfts' ? AddressNFTsResponse :
R extends 'general:address_collections' ? AddressCollectionsResponse :
R extends 'general:address_withdrawals' ? AddressWithdrawalsResponse :
R extends 'general:address_epoch_rewards' ? AddressEpochRewardsResponse :
R extends 'general:address_xstar_score' ? AddressXStarResponse :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiAddressPaginationFilters<R extends GeneralApiAddressResourceName> =
R extends 'general:addresses_metadata_search' ? AddressesMetadataSearchFilters :
R extends 'general:address_txs' | 'general:address_internal_txs' ? AddressTxsFilters :
R extends 'general:address_token_transfers' ? AddressTokenTransferFilters :
R extends 'general:address_tokens' ? AddressTokensFilter :
R extends 'general:address_nfts' ? AddressNFTTokensFilter :
R extends 'general:address_collections' ? AddressNFTTokensFilter :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiAddressPaginationSorting<R extends GeneralApiAddressResourceName> =
R extends 'general:address_txs' ? TransactionsSorting :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type {
BlocksResponse,
BlockTransactionsResponse,
Block,
BlockFilters,
BlockWithdrawalsResponse,
BlockCountdownResponse,
BlockEpoch,
BlockEpochElectionRewardDetailsResponse,
} from 'types/api/block';
import type { TTxsWithBlobsFilters } from 'types/api/txsFilters';
export const GENERAL_API_BLOCK_RESOURCES = {
blocks: {
path: '/api/v2/blocks',
filterFields: [ 'type' as const ],
paginated: true,
},
block: {
path: '/api/v2/blocks/:height_or_hash',
pathParams: [ 'height_or_hash' as const ],
},
block_txs: {
path: '/api/v2/blocks/:height_or_hash/transactions',
pathParams: [ 'height_or_hash' as const ],
filterFields: [ 'type' as const ],
paginated: true,
},
block_withdrawals: {
path: '/api/v2/blocks/:height_or_hash/withdrawals',
pathParams: [ 'height_or_hash' as const ],
filterFields: [],
paginated: true,
},
block_epoch: {
path: '/api/v2/blocks/:height_or_hash/epoch',
pathParams: [ 'height_or_hash' as const ],
filterFields: [],
},
block_election_rewards: {
path: '/api/v2/blocks/:height_or_hash/election-rewards/:reward_type',
pathParams: [ 'height_or_hash' as const, 'reward_type' as const ],
filterFields: [],
paginated: true,
},
} satisfies Record<string, ApiResource>;
export type GeneralApiBlockResourceName = `general:${ keyof typeof GENERAL_API_BLOCK_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiBlockResourcePayload<R extends GeneralApiBlockResourceName> =
R extends 'general:blocks' ? BlocksResponse :
R extends 'general:block' ? Block :
R extends 'general:block_countdown' ? BlockCountdownResponse :
R extends 'general:block_txs' ? BlockTransactionsResponse :
R extends 'general:block_withdrawals' ? BlockWithdrawalsResponse :
R extends 'general:block_epoch' ? BlockEpoch :
R extends 'general:block_election_rewards' ? BlockEpochElectionRewardDetailsResponse :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiBlockPaginationFilters<R extends GeneralApiBlockResourceName> =
R extends 'general:blocks' ? BlockFilters :
R extends 'general:block_txs' ? TTxsWithBlobsFilters :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type {
SmartContract,
SmartContractSecurityAudits,
SmartContractVerificationConfigRaw,
} from 'types/api/contract';
import type { VerifiedContractsResponse, VerifiedContractsCounters, VerifiedContractsFilters } from 'types/api/contracts';
import type { VerifiedContractsSorting } from 'types/api/verifiedContracts';
export const GENERAL_API_CONTRACT_RESOURCES = {
contract: {
path: '/api/v2/smart-contracts/:hash',
pathParams: [ 'hash' as const ],
},
contract_verification_config: {
path: '/api/v2/smart-contracts/verification/config',
},
contract_verification_via: {
path: '/api/v2/smart-contracts/:hash/verification/via/:method',
pathParams: [ 'hash' as const, 'method' as const ],
},
contract_solidity_scan_report: {
path: '/api/v2/proxy/3dparty/solidityscan/smart-contracts/:hash/report',
pathParams: [ 'hash' as const ],
},
contract_security_audits: {
path: '/api/v2/smart-contracts/:hash/audit-reports',
pathParams: [ 'hash' as const ],
},
verified_contracts: {
path: '/api/v2/smart-contracts',
filterFields: [ 'q' as const, 'filter' as const ],
paginated: true,
},
verified_contracts_counters: {
path: '/api/v2/smart-contracts/counters',
},
} satisfies Record<string, ApiResource>;
export type GeneralApiContractResourceName = `general:${ keyof typeof GENERAL_API_CONTRACT_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiContractResourcePayload<R extends GeneralApiContractResourceName> =
R extends 'general:contract' ? SmartContract :
R extends 'general:contract_solidity_scan_report' ? unknown :
R extends 'general:verified_contracts' ? VerifiedContractsResponse :
R extends 'general:verified_contracts_counters' ? VerifiedContractsCounters :
R extends 'general:contract_verification_config' ? SmartContractVerificationConfigRaw :
R extends 'general:contract_security_audits' ? SmartContractSecurityAudits :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiContractPaginationFilters<R extends GeneralApiContractResourceName> =
R extends 'general:verified_contracts' ? VerifiedContractsFilters :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiContractPaginationSorting<R extends GeneralApiContractResourceName> =
R extends 'general:verified_contracts' ? VerifiedContractsSorting :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type { GeneralApiAccountResourceName, GeneralApiAccountResourcePayload } from './account';
import { GENERAL_API_ACCOUNT_RESOURCES } from './account';
import type {
GeneralApiAddressPaginationFilters,
GeneralApiAddressPaginationSorting,
GeneralApiAddressResourceName,
GeneralApiAddressResourcePayload,
} from './address';
import { GENERAL_API_ADDRESS_RESOURCES } from './address';
import type {
GeneralApiBlockPaginationFilters,
GeneralApiBlockResourceName, GeneralApiBlockResourcePayload } from './block';
import { GENERAL_API_BLOCK_RESOURCES } from './block';
import { GENERAL_API_CONTRACT_RESOURCES } from './contract';
import type {
GeneralApiContractPaginationFilters,
GeneralApiContractPaginationSorting,
GeneralApiContractResourceName,
GeneralApiContractResourcePayload,
} from './contract';
import type {
GeneralApiMiscPaginationFilters,
GeneralApiMiscPaginationSorting,
GeneralApiMiscResourceName,
GeneralApiMiscResourcePayload,
} from './misc';
import { GENERAL_API_MISC_RESOURCES } from './misc';
import type {
GeneralApiRollupPaginationFilters,
GeneralApiRollupPaginationSorting,
GeneralApiRollupResourceName,
GeneralApiRollupResourcePayload,
} from './rollup';
import { GENERAL_API_ROLLUP_RESOURCES } from './rollup';
import type {
GeneralApiTokenPaginationFilters,
GeneralApiTokenPaginationSorting,
GeneralApiTokenResourceName,
GeneralApiTokenResourcePayload,
} from './token';
import { GENERAL_API_TOKEN_RESOURCES } from './token';
import type { GeneralApiTxResourceName, GeneralApiTxResourcePayload, GeneralApiTxPaginationFilters } from './tx';
import { GENERAL_API_TX_RESOURCES } from './tx';
import type { GeneralApiV1ResourceName, GeneralApiV1ResourcePayload } from './v1';
import { GENERAL_API_V1_RESOURCES } from './v1';
export const GENERAL_API_RESOURCES = {
...GENERAL_API_ACCOUNT_RESOURCES,
...GENERAL_API_ADDRESS_RESOURCES,
...GENERAL_API_BLOCK_RESOURCES,
...GENERAL_API_CONTRACT_RESOURCES,
...GENERAL_API_MISC_RESOURCES,
...GENERAL_API_ROLLUP_RESOURCES,
...GENERAL_API_TOKEN_RESOURCES,
...GENERAL_API_TX_RESOURCES,
...GENERAL_API_V1_RESOURCES,
} satisfies Record<string, ApiResource>;
export type GeneralApiResourceName = `general:${ keyof typeof GENERAL_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiResourcePayload<R extends GeneralApiResourceName> =
R extends GeneralApiAccountResourceName ? GeneralApiAccountResourcePayload<R> :
R extends GeneralApiAddressResourceName ? GeneralApiAddressResourcePayload<R> :
R extends GeneralApiBlockResourceName ? GeneralApiBlockResourcePayload<R> :
R extends GeneralApiContractResourceName ? GeneralApiContractResourcePayload<R> :
R extends GeneralApiMiscResourceName ? GeneralApiMiscResourcePayload<R> :
R extends GeneralApiRollupResourceName ? GeneralApiRollupResourcePayload<R> :
R extends GeneralApiTokenResourceName ? GeneralApiTokenResourcePayload<R> :
R extends GeneralApiTxResourceName ? GeneralApiTxResourcePayload<R> :
R extends GeneralApiV1ResourceName ? GeneralApiV1ResourcePayload<R> :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiPaginationFilters<R extends GeneralApiResourceName> =
R extends GeneralApiAddressResourceName ? GeneralApiAddressPaginationFilters<R> :
R extends GeneralApiBlockResourceName ? GeneralApiBlockPaginationFilters<R> :
R extends GeneralApiContractResourceName ? GeneralApiContractPaginationFilters<R> :
R extends GeneralApiMiscResourceName ? GeneralApiMiscPaginationFilters<R> :
R extends GeneralApiRollupResourceName ? GeneralApiRollupPaginationFilters<R> :
R extends GeneralApiTokenResourceName ? GeneralApiTokenPaginationFilters<R> :
R extends GeneralApiTxResourceName ? GeneralApiTxPaginationFilters<R> :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiPaginationSorting<R extends GeneralApiResourceName> =
R extends GeneralApiAddressResourceName ? GeneralApiAddressPaginationSorting<R> :
R extends GeneralApiContractResourceName ? GeneralApiContractPaginationSorting<R> :
R extends GeneralApiMiscResourceName ? GeneralApiMiscPaginationSorting<R> :
R extends GeneralApiRollupResourceName ? GeneralApiRollupPaginationSorting<R> :
R extends GeneralApiTokenResourceName ? GeneralApiTokenPaginationSorting<R> :
never;
/* eslint-enable @stylistic/indent */
This diff is collapsed.
This diff is collapsed.
import type { ApiResource } from '../../types';
import type {
TokenCounters,
TokenInfo,
TokenHolders,
TokenInventoryResponse,
TokenInstance,
TokenInstanceTransfersCount,
TokenInventoryFilters,
} from 'types/api/token';
import type { TokensResponse, TokensFilters, TokensSorting, TokenInstanceTransferResponse, TokensBridgedFilters } from 'types/api/tokens';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
export const GENERAL_API_TOKEN_RESOURCES = {
// TOKEN
token: {
path: '/api/v2/tokens/:hash',
pathParams: [ 'hash' as const ],
},
token_counters: {
path: '/api/v2/tokens/:hash/counters',
pathParams: [ 'hash' as const ],
},
token_holders: {
path: '/api/v2/tokens/:hash/holders',
pathParams: [ 'hash' as const ],
filterFields: [],
paginated: true,
},
token_transfers: {
path: '/api/v2/tokens/:hash/transfers',
pathParams: [ 'hash' as const ],
filterFields: [],
paginated: true,
},
token_inventory: {
path: '/api/v2/tokens/:hash/instances',
pathParams: [ 'hash' as const ],
filterFields: [ 'holder_address_hash' as const ],
paginated: true,
},
tokens: {
path: '/api/v2/tokens',
filterFields: [ 'q' as const, 'type' as const ],
paginated: true,
},
tokens_bridged: {
path: '/api/v2/tokens/bridged',
filterFields: [ 'q' as const, 'chain_ids' as const ],
paginated: true,
},
// TOKEN INSTANCE
token_instance: {
path: '/api/v2/tokens/:hash/instances/:id',
pathParams: [ 'hash' as const, 'id' as const ],
},
token_instance_transfers_count: {
path: '/api/v2/tokens/:hash/instances/:id/transfers-count',
pathParams: [ 'hash' as const, 'id' as const ],
},
token_instance_transfers: {
path: '/api/v2/tokens/:hash/instances/:id/transfers',
pathParams: [ 'hash' as const, 'id' as const ],
filterFields: [],
paginated: true,
},
token_instance_holders: {
path: '/api/v2/tokens/:hash/instances/:id/holders',
pathParams: [ 'hash' as const, 'id' as const ],
filterFields: [],
paginated: true,
},
token_instance_refresh_metadata: {
path: '/api/v2/tokens/:hash/instances/:id/refetch-metadata',
pathParams: [ 'hash' as const, 'id' as const ],
filterFields: [],
},
// TOKEN TRANSFERS
token_transfers_all: {
path: '/api/v2/token-transfers',
filterFields: [ 'type' as const ],
paginated: true,
},
} satisfies Record<string, ApiResource>;
export type GeneralApiTokenResourceName = `general:${ keyof typeof GENERAL_API_TOKEN_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiTokenResourcePayload<R extends GeneralApiTokenResourceName> =
R extends 'general:token' ? TokenInfo :
R extends 'general:token_counters' ? TokenCounters :
R extends 'general:token_transfers' ? TokenTransferResponse :
R extends 'general:token_holders' ? TokenHolders :
R extends 'general:token_instance' ? TokenInstance :
R extends 'general:token_instance_transfers_count' ? TokenInstanceTransfersCount :
R extends 'general:token_instance_transfers' ? TokenInstanceTransferResponse :
R extends 'general:token_instance_holders' ? TokenHolders :
R extends 'general:token_inventory' ? TokenInventoryResponse :
R extends 'general:tokens' ? TokensResponse :
R extends 'general:tokens_bridged' ? TokensResponse :
R extends 'general:token_transfers_all' ? TokenTransferResponse :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiTokenPaginationFilters<R extends GeneralApiTokenResourceName> =
R extends 'general:token_transfers' ? TokenTransferFilters :
R extends 'general:token_inventory' ? TokenInventoryFilters :
R extends 'general:tokens' ? TokensFilters :
R extends 'general:tokens_bridged' ? TokensBridgedFilters :
R extends 'general:token_transfers_all' ? TokenTransferFilters :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiTokenPaginationSorting<R extends GeneralApiTokenResourceName> =
R extends 'general:tokens' ? TokensSorting :
R extends 'general:tokens_bridged' ? TokensSorting :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type { TxBlobs } from 'types/api/blobs';
import type { InternalTransactionsResponse } from 'types/api/internalTransaction';
import type { LogsResponseTx } from 'types/api/log';
import type { RawTracesResponse } from 'types/api/rawTrace';
import type { TokenTransferResponse, TokenTransferFilters } from 'types/api/tokenTransfer';
import type {
TransactionsResponseValidated,
TransactionsResponsePending,
Transaction,
TransactionsResponseWatchlist,
TransactionsResponseWithBlobs,
TransactionsStats,
} from 'types/api/transaction';
import type { TxInterpretationResponse } from 'types/api/txInterpretation';
import type { TTxsFilters, TTxsWithBlobsFilters } from 'types/api/txsFilters';
import type { TxStateChanges } from 'types/api/txStateChanges';
export const GENERAL_API_TX_RESOURCES = {
txs_stats: {
path: '/api/v2/transactions/stats',
},
txs_validated: {
path: '/api/v2/transactions',
filterFields: [ 'filter' as const, 'type' as const, 'method' as const ],
paginated: true,
},
txs_pending: {
path: '/api/v2/transactions',
filterFields: [ 'filter' as const, 'type' as const, 'method' as const ],
paginated: true,
},
txs_with_blobs: {
path: '/api/v2/transactions',
filterFields: [ 'type' as const ],
paginated: true,
},
txs_watchlist: {
path: '/api/v2/transactions/watchlist',
filterFields: [ ],
paginated: true,
},
txs_execution_node: {
path: '/api/v2/transactions/execution-node/:hash',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
tx: {
path: '/api/v2/transactions/:hash',
pathParams: [ 'hash' as const ],
},
tx_internal_txs: {
path: '/api/v2/transactions/:hash/internal-transactions',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
tx_logs: {
path: '/api/v2/transactions/:hash/logs',
pathParams: [ 'hash' as const ],
filterFields: [ ],
paginated: true,
},
tx_token_transfers: {
path: '/api/v2/transactions/:hash/token-transfers',
pathParams: [ 'hash' as const ],
filterFields: [ 'type' as const ],
paginated: true,
},
tx_raw_trace: {
path: '/api/v2/transactions/:hash/raw-trace',
pathParams: [ 'hash' as const ],
},
tx_state_changes: {
path: '/api/v2/transactions/:hash/state-changes',
pathParams: [ 'hash' as const ],
filterFields: [],
paginated: true,
},
tx_blobs: {
path: '/api/v2/transactions/:hash/blobs',
pathParams: [ 'hash' as const ],
paginated: true,
},
tx_interpretation: {
path: '/api/v2/transactions/:hash/summary',
pathParams: [ 'hash' as const ],
},
tx_external_transactions: {
path: '/api/v2/transactions/:hash/external-transactions',
pathParams: [ 'hash' as const ],
},
internal_txs: {
path: '/api/v2/internal-transactions',
paginated: true,
},
} satisfies Record<string, ApiResource>;
export type GeneralApiTxResourceName = `general:${ keyof typeof GENERAL_API_TX_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiTxResourcePayload<R extends GeneralApiTxResourceName> =
R extends 'general:txs_stats' ? TransactionsStats :
R extends 'general:txs_validated' ? TransactionsResponseValidated :
R extends 'general:txs_pending' ? TransactionsResponsePending :
R extends 'general:txs_with_blobs' ? TransactionsResponseWithBlobs :
R extends 'general:txs_watchlist' ? TransactionsResponseWatchlist :
R extends 'general:txs_execution_node' ? TransactionsResponseValidated :
R extends 'general:tx_internal_txs' ? InternalTransactionsResponse :
R extends 'general:tx' ? Transaction :
R extends 'general:tx_logs' ? LogsResponseTx :
R extends 'general:tx_token_transfers' ? TokenTransferResponse :
R extends 'general:tx_raw_trace' ? RawTracesResponse :
R extends 'general:tx_state_changes' ? TxStateChanges :
R extends 'general:tx_blobs' ? TxBlobs :
R extends 'general:tx_interpretation' ? TxInterpretationResponse :
R extends 'general:tx_external_transactions' ? Array<string> :
R extends 'general:internal_txs' ? InternalTransactionsResponse :
never;
/* eslint-enable @stylistic/indent */
/* eslint-disable @stylistic/indent */
export type GeneralApiTxPaginationFilters<R extends GeneralApiTxResourceName> =
R extends 'general:txs_validated' | 'general:txs_pending' ? TTxsFilters :
R extends 'general:txs_with_blobs' ? TTxsWithBlobsFilters :
R extends 'general:tx_token_transfers' ? TokenTransferFilters :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../../types';
import type { BlockCountdownResponse } from 'types/api/block';
export const GENERAL_API_V1_RESOURCES = {
csv_export_txs: {
path: '/api/v1/transactions-csv',
},
csv_export_internal_txs: {
path: '/api/v1/internal-transactions-csv',
},
csv_export_token_transfers: {
path: '/api/v1/token-transfers-csv',
},
csv_export_logs: {
path: '/api/v1/logs-csv',
},
csv_export_epoch_rewards: {
path: '/api/v1/celo-election-rewards-csv',
},
graphql: {
path: '/api/v1/graphql',
},
block_countdown: {
path: '/api',
},
} satisfies Record<string, ApiResource>;
export type GeneralApiV1ResourceName = `general:${ keyof typeof GENERAL_API_V1_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type GeneralApiV1ResourcePayload<R extends GeneralApiV1ResourceName> =
R extends 'general:block_countdown' ? BlockCountdownResponse :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
import type { AddressMetadataInfo, PublicTagTypesResponse } from 'types/api/addressMetadata';
export const METADATA_API_RESOURCES = {
info: {
path: '/api/v1/metadata',
},
tags_search: {
path: '/api/v1/tags:search',
},
public_tag_types: {
path: '/api/v1/public-tag-types',
},
} satisfies Record<string, ApiResource>;
export type MetadataApiResourceName = `metadata:${ keyof typeof METADATA_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type MetadataApiResourcePayload<R extends MetadataApiResourceName> =
R extends 'metadata:info' ? AddressMetadataInfo :
R extends 'metadata:public_tag_types' ? PublicTagTypesResponse :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
import type * as rewards from '@blockscout/points-types';
export const REWARDS_API_RESOURCES = {
config: {
path: '/api/v1/config',
},
check_ref_code: {
path: '/api/v1/auth/code/:code',
pathParams: [ 'code' as const ],
},
nonce: {
path: '/api/v1/auth/nonce',
},
check_user: {
path: '/api/v1/auth/user/:address',
pathParams: [ 'address' as const ],
},
login: {
path: '/api/v1/auth/login',
},
logout: {
path: '/api/v1/auth/logout',
},
user_balances: {
path: '/api/v1/user/balances',
},
user_daily_check: {
path: '/api/v1/user/daily/check',
},
user_daily_claim: {
path: '/api/v1/user/daily/claim',
},
user_referrals: {
path: '/api/v1/user/referrals',
},
user_check_activity_pass: {
path: '/api/v1/activity/check-pass',
filterFields: [ 'address' as const ],
},
user_activity: {
path: '/api/v1/user/activity/rewards',
},
user_activity_track_tx: {
path: '/api/v1/user/activity/track/transaction',
},
user_activity_track_tx_confirm: {
path: '/api/v1/activity/track/transaction/confirm',
},
user_activity_track_contract: {
path: '/api/v1/user/activity/track/contract',
},
user_activity_track_contract_confirm: {
path: '/api/v1/activity/track/contract/confirm',
},
user_activity_track_usage: {
path: '/api/v1/user/activity/track/usage',
},
instances: {
path: '/api/v1/instances',
},
} satisfies Record<string, ApiResource>;
export type RewardsApiResourceName = `rewards:${ keyof typeof REWARDS_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type RewardsApiResourcePayload<R extends RewardsApiResourceName> =
R extends 'rewards:config' ? rewards.GetConfigResponse :
R extends 'rewards:check_ref_code' ? rewards.AuthCodeResponse :
R extends 'rewards:nonce' ? rewards.AuthNonceResponse :
R extends 'rewards:check_user' ? rewards.AuthUserResponse :
R extends 'rewards:login' ? rewards.AuthLoginResponse :
R extends 'rewards:user_balances' ? rewards.GetUserBalancesResponse :
R extends 'rewards:user_daily_check' ? rewards.DailyRewardCheckResponse :
R extends 'rewards:user_daily_claim' ? rewards.DailyRewardClaimResponse :
R extends 'rewards:user_referrals' ? rewards.GetReferralDataResponse :
R extends 'rewards:user_check_activity_pass' ? rewards.CheckActivityPassResponse :
R extends 'rewards:user_activity' ? rewards.GetActivityRewardsResponse :
R extends 'rewards:user_activity_track_tx' ? rewards.PreSubmitTransactionResponse :
R extends 'rewards:user_activity_track_contract' ? rewards.PreVerifyContractResponse :
R extends 'rewards:instances' ? rewards.GetInstancesResponse :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
import type * as stats from '@blockscout/stats-types';
export const STATS_API_RESOURCES = {
counters: {
path: '/api/v1/counters',
},
lines: {
path: '/api/v1/lines',
},
line: {
path: '/api/v1/lines/:id',
pathParams: [ 'id' as const ],
},
pages_main: {
path: '/api/v1/pages/main',
},
pages_transactions: {
path: '/api/v1/pages/transactions',
},
pages_contracts: {
path: '/api/v1/pages/contracts',
},
} satisfies Record<string, ApiResource>;
export type StatsApiResourceName = `stats:${ keyof typeof STATS_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type StatsApiResourcePayload<R extends StatsApiResourceName> =
R extends 'stats:counters' ? stats.Counters :
R extends 'stats:lines' ? stats.LineCharts :
R extends 'stats:line' ? stats.LineChart :
R extends 'stats:pages_main' ? stats.MainPageStats :
R extends 'stats:pages_transactions' ? stats.TransactionsPageStats :
R extends 'stats:pages_contracts' ? stats.ContractsPageStats :
never;
/* eslint-enable @stylistic/indent */
import type { ApiResource } from '../types';
export type IsPaginated<R extends ApiResource> = R extends { paginated: true } ? true : false;
import type { ApiResource } from '../types';
import type * as visualizer from '@blockscout/visualizer-types';
export const VISUALIZE_API_RESOURCES = {
solidity_contract: {
path: '/api/v1/solidity\\:visualize-contracts',
},
} satisfies Record<string, ApiResource>;
export type VisualizeApiResourceName = `visualize:${ keyof typeof VISUALIZE_API_RESOURCES }`;
/* eslint-disable @stylistic/indent */
export type VisualizeApiResourcePayload<R extends VisualizeApiResourceName> =
R extends 'visualize:solidity_contract' ? visualizer.VisualizeResponse :
never;
/* eslint-enable @stylistic/indent */
export type ApiName = 'general' | 'admin' | 'bens' | 'contractInfo' | 'metadata' | 'rewards' | 'stats' | 'visualize';
export interface ApiResource {
path: string;
pathParams?: Array<string>;
filterFields?: Array<string>;
paginated?: boolean;
headers?: RequestInit['headers'];
}
...@@ -13,8 +13,8 @@ import type { Params as FetchParams } from 'lib/hooks/useFetch'; ...@@ -13,8 +13,8 @@ import type { Params as FetchParams } from 'lib/hooks/useFetch';
import useFetch from 'lib/hooks/useFetch'; import useFetch from 'lib/hooks/useFetch';
import buildUrl from './buildUrl'; import buildUrl from './buildUrl';
import { RESOURCES } from './resources'; import getResourceParams from './getResourceParams';
import type { ApiResource, ResourceName, ResourcePathParams } from './resources'; import type { ResourceName, ResourcePathParams } from './resources';
export interface Params<R extends ResourceName> { export interface Params<R extends ResourceName> {
pathParams?: ResourcePathParams<R>; pathParams?: ResourcePathParams<R>;
...@@ -26,7 +26,7 @@ export interface Params<R extends ResourceName> { ...@@ -26,7 +26,7 @@ export interface Params<R extends ResourceName> {
export default function useApiFetch() { export default function useApiFetch() {
const fetch = useFetch(); const fetch = useFetch();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { token: csrfToken } = queryClient.getQueryData<CsrfData>(getResourceKey('csrf')) || {}; const { token: csrfToken } = queryClient.getQueryData<CsrfData>(getResourceKey('general:csrf')) || {};
return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>( return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>(
resourceName: R, resourceName: R,
...@@ -34,12 +34,12 @@ export default function useApiFetch() { ...@@ -34,12 +34,12 @@ export default function useApiFetch() {
) => { ) => {
const apiToken = cookies.get(cookies.NAMES.API_TOKEN); const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
const resource: ApiResource = RESOURCES[resourceName]; const { api, apiName, resource } = getResourceParams(resourceName);
const url = buildUrl(resourceName, pathParams, queryParams); const url = buildUrl(resourceName, pathParams, queryParams);
const withBody = isBodyAllowed(fetchParams?.method); const withBody = isBodyAllowed(fetchParams?.method);
const headers = pickBy({ const headers = pickBy({
'x-endpoint': resource.endpoint && isNeedProxy() ? resource.endpoint : undefined, 'x-endpoint': api.endpoint && apiName !== 'general' && isNeedProxy() ? api.endpoint : undefined,
Authorization: resource.endpoint && resource.needAuth ? apiToken : undefined, Authorization: [ 'admin', 'contractInfo' ].includes(apiName) ? apiToken : undefined,
'x-csrf-token': withBody && csrfToken ? csrfToken : undefined, 'x-csrf-token': withBody && csrfToken ? csrfToken : undefined,
...resource.headers, ...resource.headers,
...fetchParams?.headers, ...fetchParams?.headers,
......
import type { InfiniteData, QueryKey, UseInfiniteQueryResult, UseInfiniteQueryOptions } from '@tanstack/react-query'; import type { InfiniteData, QueryKey, UseInfiniteQueryResult, UseInfiniteQueryOptions } from '@tanstack/react-query';
import { useInfiniteQuery } from '@tanstack/react-query'; import { useInfiniteQuery } from '@tanstack/react-query';
import type { PaginatedResources, ResourceError, ResourcePayload } from 'lib/api/resources'; import type { PaginatedResourceName, ResourceError, ResourcePayload } from './resources';
import useApiFetch from 'lib/api/useApiFetch'; import useApiFetch from './useApiFetch';
import type { Params as ApiFetchParams } from 'lib/api/useApiFetch'; import type { Params as ApiFetchParams } from './useApiFetch';
import { getResourceKey } from './useApiQuery'; import { getResourceKey } from './useApiQuery';
type TQueryData<R extends PaginatedResources> = ResourcePayload<R>; type TQueryData<R extends PaginatedResourceName> = ResourcePayload<R>;
type TError = ResourceError<unknown>; type TError = ResourceError<unknown>;
type TPageParam<R extends PaginatedResources> = ApiFetchParams<R>['queryParams'] | null; type TPageParam<R extends PaginatedResourceName> = ApiFetchParams<R>['queryParams'] | null;
export interface Params<R extends PaginatedResources> { export interface Params<R extends PaginatedResourceName> {
resourceName: R; resourceName: R;
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
queryOptions?: Omit<UseInfiniteQueryOptions<TQueryData<R>, TError, InfiniteData<TQueryData<R>>, TQueryData<R>, QueryKey, TPageParam<R>>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>; queryOptions?: Omit<UseInfiniteQueryOptions<TQueryData<R>, TError, InfiniteData<TQueryData<R>>, TQueryData<R>, QueryKey, TPageParam<R>>, 'queryKey' | 'queryFn' | 'getNextPageParam' | 'initialPageParam'>;
pathParams?: ApiFetchParams<R>['pathParams']; pathParams?: ApiFetchParams<R>['pathParams'];
} }
type ReturnType<Resource extends PaginatedResources> = UseInfiniteQueryResult<InfiniteData<ResourcePayload<Resource>>, ResourceError<unknown>>; type ReturnType<Resource extends PaginatedResourceName> = UseInfiniteQueryResult<InfiniteData<ResourcePayload<Resource>>, ResourceError<unknown>>;
export default function useApiInfiniteQuery<R extends PaginatedResources>({ export default function useApiInfiniteQuery<R extends PaginatedResourceName>({
resourceName, resourceName,
queryOptions, queryOptions,
pathParams, pathParams,
......
...@@ -4,6 +4,8 @@ import React from 'react'; ...@@ -4,6 +4,8 @@ import React from 'react';
import getErrorObjPayload from 'lib/errors/getErrorObjPayload'; import getErrorObjPayload from 'lib/errors/getErrorObjPayload';
import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode'; import getErrorObjStatusCode from 'lib/errors/getErrorObjStatusCode';
import type { ResourceName } from './resources';
export const retry = (failureCount: number, error: unknown) => { export const retry = (failureCount: number, error: unknown) => {
const errorPayload = getErrorObjPayload<{ status: number }>(error); const errorPayload = getErrorObjPayload<{ status: number }>(error);
const status = errorPayload?.status || getErrorObjStatusCode(error); const status = errorPayload?.status || getErrorObjStatusCode(error);
...@@ -28,13 +30,14 @@ export default function useQueryClientConfig() { ...@@ -28,13 +30,14 @@ export default function useQueryClientConfig() {
return false; return false;
} }
const EXTERNAL_API_RESOURCES = [ const EXTERNAL_API_RESOURCES: Array<ResourceName | 'safe_transaction_api' | 'gas_hawk_saving_potential'> = [
'general:contract_solidity_scan_report',
'general:address_xstar_score',
'general:noves_transaction',
'general:noves_address_history',
'general:noves_describe_txs',
// these resources are not proxied by the backend
'safe_transaction_api', 'safe_transaction_api',
'contract_solidity_scan_report',
'address_xstar_score',
'noves_transaction',
'noves_address_history',
'noves_describe_txs',
'gas_hawk_saving_potential', 'gas_hawk_saving_potential',
]; ];
const isExternalApiResource = EXTERNAL_API_RESOURCES.some((resource) => query.queryKey[0] === resource); const isExternalApiResource = EXTERNAL_API_RESOURCES.some((resource) => query.queryKey[0] === resource);
......
...@@ -77,8 +77,8 @@ function getMessageToSign(address: string, nonce: string, isLogin?: boolean, ref ...@@ -77,8 +77,8 @@ function getMessageToSign(address: string, nonce: string, isLogin?: boolean, ref
const referralText = refCode ? ` Referral code: ${ refCode }` : ''; const referralText = refCode ? ` Referral code: ${ refCode }` : '';
const body = isLogin ? signInText : signUpText + referralText; const body = isLogin ? signInText : signUpText + referralText;
const urlObj = window.location.hostname === 'localhost' && feature.isEnabled ? const urlObj = window.location.hostname === 'localhost' && config.apis.rewards ?
new URL(feature.api.endpoint) : new URL(config.apis.rewards.endpoint) :
window.location; window.location;
return [ return [
...@@ -146,18 +146,18 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -146,18 +146,18 @@ export function RewardsContextProvider({ children }: Props) {
{ headers: { Authorization: `Bearer ${ apiToken }` } }, { headers: { Authorization: `Bearer ${ apiToken }` } },
], [ apiToken ]); ], [ apiToken ]);
const balancesQuery = useApiQuery('rewards_user_balances', { queryOptions, fetchParams }); const balancesQuery = useApiQuery('rewards:user_balances', { queryOptions, fetchParams });
const dailyRewardQuery = useApiQuery('rewards_user_daily_check', { queryOptions, fetchParams }); const dailyRewardQuery = useApiQuery('rewards:user_daily_check', { queryOptions, fetchParams });
const referralsQuery = useApiQuery('rewards_user_referrals', { queryOptions, fetchParams }); const referralsQuery = useApiQuery('rewards:user_referrals', { queryOptions, fetchParams });
const rewardsConfigQuery = useApiQuery('rewards_config', { queryOptions: { enabled: feature.isEnabled } }); const rewardsConfigQuery = useApiQuery('rewards:config', { queryOptions: { enabled: feature.isEnabled } });
const checkUserQuery = useApiQuery('rewards_check_user', { queryOptions: { enabled: feature.isEnabled }, pathParams: { address } }); const checkUserQuery = useApiQuery('rewards:check_user', { queryOptions: { enabled: feature.isEnabled }, pathParams: { address } });
// Reset queries when the API token is removed // Reset queries when the API token is removed
useEffect(() => { useEffect(() => {
if (isInitialized && !apiToken) { if (isInitialized && !apiToken) {
queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_balances'), exact: true }); queryClient.resetQueries({ queryKey: getResourceKey('rewards:user_balances'), exact: true });
queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_daily_check'), exact: true }); queryClient.resetQueries({ queryKey: getResourceKey('rewards:user_daily_check'), exact: true });
queryClient.resetQueries({ queryKey: getResourceKey('rewards_user_referrals'), exact: true }); queryClient.resetQueries({ queryKey: getResourceKey('rewards:user_referrals'), exact: true });
} }
}, [ isInitialized, apiToken, queryClient ]); }, [ isInitialized, apiToken, queryClient ]);
...@@ -203,9 +203,9 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -203,9 +203,9 @@ export function RewardsContextProvider({ children }: Props) {
throw new Error(); throw new Error();
} }
const [ nonceResponse, checkCodeResponse ] = await Promise.all([ const [ nonceResponse, checkCodeResponse ] = await Promise.all([
apiFetch('rewards_nonce') as Promise<rewards.AuthNonceResponse>, apiFetch('rewards:nonce') as Promise<rewards.AuthNonceResponse>,
refCode ? refCode ?
apiFetch('rewards_check_ref_code', { pathParams: { code: refCode } }) as Promise<rewards.AuthCodeResponse> : apiFetch('rewards:check_ref_code', { pathParams: { code: refCode } }) as Promise<rewards.AuthCodeResponse> :
Promise.resolve({ valid: true, reward: undefined }), Promise.resolve({ valid: true, reward: undefined }),
]); ]);
if (!checkCodeResponse.valid) { if (!checkCodeResponse.valid) {
...@@ -217,7 +217,7 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -217,7 +217,7 @@ export function RewardsContextProvider({ children }: Props) {
await switchChainAsync({ chainId: Number(config.chain.id) }); await switchChainAsync({ chainId: Number(config.chain.id) });
const message = getMessageToSign(address, nonceResponse.nonce, checkUserQuery.data?.exists, refCode); const message = getMessageToSign(address, nonceResponse.nonce, checkUserQuery.data?.exists, refCode);
const signature = await signMessageAsync({ message }); const signature = await signMessageAsync({ message });
const loginResponse = await apiFetch('rewards_login', { const loginResponse = await apiFetch('rewards:login', {
fetchParams: { fetchParams: {
method: 'POST', method: 'POST',
body: { body: {
...@@ -241,7 +241,7 @@ export function RewardsContextProvider({ children }: Props) { ...@@ -241,7 +241,7 @@ export function RewardsContextProvider({ children }: Props) {
// Claim daily reward // Claim daily reward
const claim = useCallback(async() => { const claim = useCallback(async() => {
try { try {
await apiFetch('rewards_user_daily_claim', { await apiFetch('rewards:user_daily_claim', {
fetchParams: { fetchParams: {
method: 'POST', method: 'POST',
...fetchParams, ...fetchParams,
......
...@@ -10,10 +10,10 @@ export default function useGetCsrfToken() { ...@@ -10,10 +10,10 @@ export default function useGetCsrfToken() {
const nodeApiFetch = useFetch(); const nodeApiFetch = useFetch();
return useQuery({ return useQuery({
queryKey: getResourceKey('csrf'), queryKey: getResourceKey('general:csrf'),
queryFn: async() => { queryFn: async() => {
if (!isNeedProxy()) { if (!isNeedProxy()) {
const url = buildUrl('csrf'); const url = buildUrl('general:csrf');
const apiResponse = await fetch(url, { credentials: 'include' }); const apiResponse = await fetch(url, { credentials: 'include' });
const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf'); const csrfFromHeader = apiResponse.headers.get('x-bs-account-csrf');
......
...@@ -13,11 +13,11 @@ const feature = config.features.rewards; ...@@ -13,11 +13,11 @@ const feature = config.features.rewards;
const LAST_EXPLORE_TIME_KEY = 'rewards_activity_last_explore_time'; const LAST_EXPLORE_TIME_KEY = 'rewards_activity_last_explore_time';
type RewardsActivityEndpoint = type RewardsActivityEndpoint =
| 'rewards_user_activity_track_tx' | 'rewards:user_activity_track_tx'
| 'rewards_user_activity_track_tx_confirm' | 'rewards:user_activity_track_tx_confirm'
| 'rewards_user_activity_track_contract' | 'rewards:user_activity_track_contract'
| 'rewards_user_activity_track_contract_confirm' | 'rewards:user_activity_track_contract_confirm'
| 'rewards_user_activity_track_usage'; | 'rewards:user_activity_track_usage';
export default function useRewardsActivity() { export default function useRewardsActivity() {
const { apiToken } = useRewardsContext(); const { apiToken } = useRewardsContext();
...@@ -25,7 +25,7 @@ export default function useRewardsActivity() { ...@@ -25,7 +25,7 @@ export default function useRewardsActivity() {
const lastExploreTime = useRef<number>(0); const lastExploreTime = useRef<number>(0);
const profileQuery = useProfileQuery(); const profileQuery = useProfileQuery();
const checkActivityPassQuery = useApiQuery('rewards_user_check_activity_pass', { const checkActivityPassQuery = useApiQuery('rewards:user_check_activity_pass', {
queryOptions: { queryOptions: {
enabled: feature.isEnabled && Boolean(apiToken) && Boolean(profileQuery.data?.address_hash), enabled: feature.isEnabled && Boolean(apiToken) && Boolean(profileQuery.data?.address_hash),
}, },
...@@ -61,7 +61,7 @@ export default function useRewardsActivity() { ...@@ -61,7 +61,7 @@ export default function useRewardsActivity() {
const trackTransaction = useCallback(async(from: string, to: string) => { const trackTransaction = useCallback(async(from: string, to: string) => {
return ( return (
await makeRequest('rewards_user_activity_track_tx', { await makeRequest('rewards:user_activity_track_tx', {
from_address: from, from_address: from,
to_address: to, to_address: to,
chain_id: config.chain.id ?? '', chain_id: config.chain.id ?? '',
...@@ -70,12 +70,12 @@ export default function useRewardsActivity() { ...@@ -70,12 +70,12 @@ export default function useRewardsActivity() {
}, [ makeRequest ]); }, [ makeRequest ]);
const trackTransactionConfirm = useCallback((hash: string, token: string) => const trackTransactionConfirm = useCallback((hash: string, token: string) =>
makeRequest('rewards_user_activity_track_tx_confirm', { tx_hash: hash, token }), makeRequest('rewards:user_activity_track_tx_confirm', { tx_hash: hash, token }),
[ makeRequest ], [ makeRequest ],
); );
const trackContract = useCallback(async(address: string) => const trackContract = useCallback(async(address: string) =>
makeRequest('rewards_user_activity_track_contract', { makeRequest('rewards:user_activity_track_contract', {
address, address,
chain_id: config.chain.id ?? '', chain_id: config.chain.id ?? '',
}), }),
...@@ -99,7 +99,7 @@ export default function useRewardsActivity() { ...@@ -99,7 +99,7 @@ export default function useRewardsActivity() {
} catch {} } catch {}
} }
return makeRequest('rewards_user_activity_track_usage', { return makeRequest('rewards:user_activity_track_usage', {
action, action,
chain_id: config.chain.id ?? '', chain_id: config.chain.id ?? '',
}); });
......
...@@ -11,7 +11,7 @@ interface Params { ...@@ -11,7 +11,7 @@ interface Params {
hash: string; hash: string;
} }
const RESOURCE_NAME = 'contract_solidity_scan_report'; const RESOURCE_NAME = 'general:contract_solidity_scan_report';
const ERROR_NAME = 'Invalid response schema'; const ERROR_NAME = 'Invalid response schema';
export default function useFetchReport({ hash }: Params) { export default function useFetchReport({ hash }: Params) {
......
...@@ -10,7 +10,7 @@ export default function useAccountWithDomain(isEnabled: boolean) { ...@@ -10,7 +10,7 @@ export default function useAccountWithDomain(isEnabled: boolean) {
const isQueryEnabled = config.features.nameService.isEnabled && Boolean(address) && Boolean(isEnabled); const isQueryEnabled = config.features.nameService.isEnabled && Boolean(address) && Boolean(isEnabled);
const domainQuery = useApiQuery('address_domain', { const domainQuery = useApiQuery('bens:address_domain', {
pathParams: { pathParams: {
chainId: config.chain.id, chainId: config.chain.id,
address, address,
......
...@@ -20,7 +20,7 @@ const wagmi = (() => { ...@@ -20,7 +20,7 @@ const wagmi = (() => {
[currentChain.id]: fallback( [currentChain.id]: fallback(
config.chain.rpcUrls config.chain.rpcUrls
.map((url) => http(url)) .map((url) => http(url))
.concat(http(`${ config.api.endpoint }/api/eth-rpc`)), .concat(http(`${ config.apis.general.endpoint }/api/eth-rpc`)),
), ),
...(parentChain ? { [parentChain.id]: http(parentChain.rpcUrls.default.http[0]) } : {}), ...(parentChain ? { [parentChain.id]: http(parentChain.rpcUrls.default.http[0]) } : {}),
}, },
......
...@@ -9,7 +9,7 @@ interface Params { ...@@ -9,7 +9,7 @@ interface Params {
hash: string; hash: string;
} }
const RESOURCE_NAME = 'address_xstar_score'; const RESOURCE_NAME = 'general:address_xstar_score';
const ERROR_NAME = 'Invalid response schema'; const ERROR_NAME = 'Invalid response schema';
export default function useFetchXStarScore({ hash }: Params) { export default function useFetchXStarScore({ hash }: Params) {
......
...@@ -40,15 +40,8 @@ export function app(): CspDev.DirectiveDescriptor { ...@@ -40,15 +40,8 @@ export function app(): CspDev.DirectiveDescriptor {
config.app.isDev ? 'ws://localhost:3000/_next/webpack-hmr' : '', config.app.isDev ? 'ws://localhost:3000/_next/webpack-hmr' : '',
// APIs // APIs
config.api.endpoint, ...Object.values(config.apis).filter(Boolean).map((api) => api.endpoint),
config.api.socket, config.apis.general.socketEndpoint,
getFeaturePayload(config.features.stats)?.api.endpoint,
getFeaturePayload(config.features.sol2uml)?.api.endpoint,
getFeaturePayload(config.features.verifiedTokens)?.api.endpoint,
getFeaturePayload(config.features.addressVerification)?.api.endpoint,
getFeaturePayload(config.features.nameService)?.api.endpoint,
getFeaturePayload(config.features.addressMetadata)?.api.endpoint,
getFeaturePayload(config.features.rewards)?.api.endpoint,
// chain RPC server // chain RPC server
...config.chain.rpcUrls, ...config.chain.rpcUrls,
......
import { compile } from 'path-to-regexp'; import { compile } from 'path-to-regexp';
import config from 'configs/app'; import getResourceParams from 'lib/api/getResourceParams';
import { RESOURCES } from 'lib/api/resources'; import type { ResourceName } from 'lib/api/resources';
import type { ApiResource, ResourceName } from 'lib/api/resources';
export default function buildUrl( export default function buildUrl(
_resource: ApiResource | ResourceName, _resource: ResourceName,
pathParams?: Record<string, string | undefined>, pathParams?: Record<string, string | undefined>,
queryParams?: Record<string, string | number | undefined>, queryParams?: Record<string, string | number | undefined>,
) { ) {
const resource: ApiResource = typeof _resource === 'string' ? RESOURCES[_resource] : _resource; const { resource, api } = getResourceParams(_resource);
const baseUrl = resource.endpoint || config.api.endpoint; const baseUrl = api.endpoint;
const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath; const basePath = api.basePath ?? '';
const path = basePath + resource.path; const path = basePath + resource.path;
const url = new URL(compile(path)(pathParams), baseUrl); const url = new URL(compile(path)(pathParams), baseUrl);
......
...@@ -3,21 +3,14 @@ import fetch, { AbortError } from 'node-fetch'; ...@@ -3,21 +3,14 @@ import fetch, { AbortError } from 'node-fetch';
import buildUrl from 'nextjs/utils/buildUrl'; import buildUrl from 'nextjs/utils/buildUrl';
import { httpLogger } from 'nextjs/utils/logger'; import { httpLogger } from 'nextjs/utils/logger';
import { RESOURCES } from 'lib/api/resources';
import type { ResourceName, ResourcePathParams, ResourcePayload } from 'lib/api/resources'; import type { ResourceName, ResourcePathParams, ResourcePayload } from 'lib/api/resources';
import metrics from 'lib/monitoring/metrics'; import metrics from 'lib/monitoring/metrics';
import { SECOND } from 'toolkit/utils/consts'; import { SECOND } from 'toolkit/utils/consts';
type Params<R extends ResourceName> = ( type Params<R extends ResourceName> = {
{
resource: R; resource: R;
pathParams?: ResourcePathParams<R>; pathParams?: ResourcePathParams<R>;
queryParams?: Record<string, string | number | undefined>; queryParams?: Record<string, string | number | undefined>;
} | {
url: string;
route: string;
}
) & {
timeout?: number; timeout?: number;
}; };
...@@ -27,15 +20,14 @@ export default async function fetchApi<R extends ResourceName = never, S = Resou ...@@ -27,15 +20,14 @@ export default async function fetchApi<R extends ResourceName = never, S = Resou
controller.abort(); controller.abort();
}, params.timeout || SECOND); }, params.timeout || SECOND);
const url = 'url' in params ? params.url : buildUrl(params.resource, params.pathParams, params.queryParams); const url = buildUrl(params.resource, params.pathParams, params.queryParams);
const route = 'route' in params ? params.route : RESOURCES[params.resource]['path'];
const end = metrics?.apiRequestDuration.startTimer(); const end = metrics?.apiRequestDuration.startTimer();
try { try {
const response = await fetch(url, { signal: controller.signal }); const response = await fetch(url, { signal: controller.signal });
const duration = end?.({ route, code: response.status }); const duration = end?.({ route: params.resource, code: response.status });
if (response.status === 200) { if (response.status === 200) {
httpLogger.logger.info({ message: 'API fetch', url, code: response.status, duration }); httpLogger.logger.info({ message: 'API fetch', url, code: response.status, duration });
} else { } else {
...@@ -45,7 +37,7 @@ export default async function fetchApi<R extends ResourceName = never, S = Resou ...@@ -45,7 +37,7 @@ export default async function fetchApi<R extends ResourceName = never, S = Resou
return await response.json() as Promise<S>; return await response.json() as Promise<S>;
} catch (error) { } catch (error) {
const code = error instanceof AbortError ? 504 : 500; const code = error instanceof AbortError ? 504 : 500;
const duration = end?.({ route, code }); const duration = end?.({ route: params.resource, code });
httpLogger.logger.error({ message: 'API fetch', url, code, duration }); httpLogger.logger.error({ message: 'API fetch', url, code, duration });
} finally { } finally {
clearTimeout(timeout); clearTimeout(timeout);
......
...@@ -82,7 +82,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { ...@@ -82,7 +82,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<QueryClientProvider client={ queryClient }> <QueryClientProvider client={ queryClient }>
<GrowthBookProvider growthbook={ growthBook }> <GrowthBookProvider growthbook={ growthBook }>
<ScrollDirectionProvider> <ScrollDirectionProvider>
<SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }> <SocketProvider url={ `${ config.apis.general.socketEndpoint }${ config.apis.general.basePath ?? '' }/socket/v2` }>
<RewardsContextProvider> <RewardsContextProvider>
<MarketplaceContextProvider> <MarketplaceContextProvider>
<SettingsContextProvider> <SettingsContextProvider>
......
...@@ -32,7 +32,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as ...@@ -32,7 +32,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as
if (botInfo?.type === 'social_preview') { if (botInfo?.type === 'social_preview') {
const addressData = await fetchApi({ const addressData = await fetchApi({
resource: 'address', resource: 'general:address',
pathParams: { hash: getQueryParamString(ctx.query.hash) }, pathParams: { hash: getQueryParamString(ctx.query.hash) },
timeout: 1_000, timeout: 1_000,
}); });
......
...@@ -7,7 +7,7 @@ import { httpLogger } from 'nextjs/utils/logger'; ...@@ -7,7 +7,7 @@ import { httpLogger } from 'nextjs/utils/logger';
export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) { export default async function csrfHandler(_req: NextApiRequest, res: NextApiResponse) {
httpLogger(_req, res); httpLogger(_req, res);
const url = buildUrl('csrf'); const url = buildUrl('general:csrf');
const response = await fetchFactory(_req)(url); const response = await fetchFactory(_req)(url);
if (response.status === 200) { if (response.status === 200) {
......
...@@ -13,7 +13,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => { ...@@ -13,7 +13,7 @@ const handler = async(nextReq: NextApiRequest, nextRes: NextApiResponse) => {
const url = new URL( const url = new URL(
nextReq.url.replace(/^\/node-api\/proxy/, ''), nextReq.url.replace(/^\/node-api\/proxy/, ''),
nextReq.headers['x-endpoint']?.toString() || appConfig.api.endpoint, nextReq.headers['x-endpoint']?.toString() || appConfig.apis.general.endpoint,
); );
const apiRes = await fetchFactory(nextReq)( const apiRes = await fetchFactory(nextReq)(
url.toString(), url.toString(),
......
import type { GetServerSideProps } from 'next'; import type { GetServerSideProps } from 'next';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import fetch from 'node-fetch';
import React from 'react'; import React from 'react';
import type { NextPageWithLayout } from 'nextjs/types'; import type { NextPageWithLayout } from 'nextjs/types';
...@@ -49,19 +50,25 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as ...@@ -49,19 +50,25 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as
const appData = await(async() => { const appData = await(async() => {
if ('configUrl' in feature) { if ('configUrl' in feature) {
const appList = await fetchApi<never, Array<MarketplaceAppOverview>>({ const controller = new AbortController();
url: config.app.baseUrl + feature.configUrl, const timeout = setTimeout(() => {
route: '/marketplace_config', controller.abort();
timeout: 1_000, }, 1_000);
});
try {
const response = await fetch(feature.configUrl, { signal: controller.signal });
const appList = await response.json() as Array<MarketplaceAppOverview>;
clearTimeout(timeout);
if (appList && Array.isArray(appList)) { if (appList && Array.isArray(appList)) {
return appList.find(app => app.id === getQueryParamString(ctx.query.id)); return appList.find(app => app.id === getQueryParamString(ctx.query.id));
} }
} catch (error) {} finally {
clearTimeout(timeout);
}
} else { } else {
return await fetchApi({ return await fetchApi({
resource: 'marketplace_dapp', resource: 'admin:marketplace_dapp',
pathParams: { dappId: getQueryParamString(ctx.query.id), chainId: config.chain.id }, pathParams: { dappId: getQueryParamString(ctx.query.id), chainId: config.chain.id },
timeout: 1_000, timeout: 1_000,
}); });
......
...@@ -36,7 +36,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as ...@@ -36,7 +36,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as
(config.meta.og.enhancedDataEnabled && detectBotRequest(ctx.req)?.type === 'social_preview') (config.meta.og.enhancedDataEnabled && detectBotRequest(ctx.req)?.type === 'social_preview')
) { ) {
const chartData = await fetchApi({ const chartData = await fetchApi({
resource: 'stats_line', resource: 'stats:line',
pathParams: { id: getQueryParamString(ctx.query.id) }, pathParams: { id: getQueryParamString(ctx.query.id) },
queryParams: { from: dayjs().format('YYYY-MM-DD'), to: dayjs().format('YYYY-MM-DD') }, queryParams: { from: dayjs().format('YYYY-MM-DD'), to: dayjs().format('YYYY-MM-DD') },
timeout: 1000, timeout: 1000,
......
...@@ -33,7 +33,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as ...@@ -33,7 +33,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as
(config.meta.og.enhancedDataEnabled && detectBotRequest(ctx.req)?.type === 'social_preview') (config.meta.og.enhancedDataEnabled && detectBotRequest(ctx.req)?.type === 'social_preview')
) { ) {
const tokenData = await fetchApi({ const tokenData = await fetchApi({
resource: 'token', resource: 'general:token',
pathParams: { hash: getQueryParamString(ctx.query.hash) }, pathParams: { hash: getQueryParamString(ctx.query.hash) },
timeout: 500, timeout: 500,
}); });
......
...@@ -32,7 +32,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as ...@@ -32,7 +32,7 @@ export const getServerSideProps: GetServerSideProps<Props<typeof pathname>> = as
if (botInfo?.type === 'social_preview') { if (botInfo?.type === 'social_preview') {
const tokenData = await fetchApi({ const tokenData = await fetchApi({
resource: 'token', resource: 'general:token',
pathParams: { hash: getQueryParamString(ctx.query.hash) }, pathParams: { hash: getQueryParamString(ctx.query.hash) },
timeout: 1_000, timeout: 1_000,
}); });
......
...@@ -85,10 +85,10 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = { ...@@ -85,10 +85,10 @@ export const ENVS_MAP: Record<string, Array<[string, string]>> = {
[ 'NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED', 'true' ], [ 'NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED', 'true' ],
], ],
nameService: [ nameService: [
[ 'NEXT_PUBLIC_NAME_SERVICE_API_HOST', 'https://localhost:3101' ], [ 'NEXT_PUBLIC_NAME_SERVICE_API_HOST', 'https://localhost:3008' ],
], ],
rewardsService: [ rewardsService: [
[ 'NEXT_PUBLIC_REWARDS_SERVICE_API_HOST', 'http://localhost:3003' ], [ 'NEXT_PUBLIC_REWARDS_SERVICE_API_HOST', 'http://localhost:3009' ],
], ],
addressBech32Format: [ addressBech32Format: [
[ 'NEXT_PUBLIC_ADDRESS_FORMAT', '["bech32","base16"]' ], [ 'NEXT_PUBLIC_ADDRESS_FORMAT', '["bech32","base16"]' ],
......
...@@ -66,26 +66,26 @@ export const TOKEN_HOLDER_ERC_1155: TokenHolder = { ...@@ -66,26 +66,26 @@ export const TOKEN_HOLDER_ERC_1155: TokenHolder = {
export const getTokenHoldersStub = (type?: TokenType, pagination: TokenHoldersPagination | null = null): TokenHolders => { export const getTokenHoldersStub = (type?: TokenType, pagination: TokenHoldersPagination | null = null): TokenHolders => {
switch (type) { switch (type) {
case 'ERC-721': case 'ERC-721':
return generateListStub<'token_holders'>(TOKEN_HOLDER_ERC_20, 50, { next_page_params: pagination }); return generateListStub<'general:token_holders'>(TOKEN_HOLDER_ERC_20, 50, { next_page_params: pagination });
case 'ERC-1155': case 'ERC-1155':
return generateListStub<'token_holders'>(TOKEN_HOLDER_ERC_1155, 50, { next_page_params: pagination }); return generateListStub<'general:token_holders'>(TOKEN_HOLDER_ERC_1155, 50, { next_page_params: pagination });
case 'ERC-404': case 'ERC-404':
return generateListStub<'token_holders'>(TOKEN_HOLDER_ERC_1155, 50, { next_page_params: pagination }); return generateListStub<'general:token_holders'>(TOKEN_HOLDER_ERC_1155, 50, { next_page_params: pagination });
default: default:
return generateListStub<'token_holders'>(TOKEN_HOLDER_ERC_20, 50, { next_page_params: pagination }); return generateListStub<'general:token_holders'>(TOKEN_HOLDER_ERC_20, 50, { next_page_params: pagination });
} }
}; };
export const getTokenInstanceHoldersStub = (type?: TokenType, pagination: TokenHoldersPagination | null = null): TokenHolders => { export const getTokenInstanceHoldersStub = (type?: TokenType, pagination: TokenHoldersPagination | null = null): TokenHolders => {
switch (type) { switch (type) {
case 'ERC-721': case 'ERC-721':
return generateListStub<'token_instance_holders'>(TOKEN_HOLDER_ERC_20, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_holders'>(TOKEN_HOLDER_ERC_20, 10, { next_page_params: pagination });
case 'ERC-1155': case 'ERC-1155':
return generateListStub<'token_instance_holders'>(TOKEN_HOLDER_ERC_1155, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_holders'>(TOKEN_HOLDER_ERC_1155, 10, { next_page_params: pagination });
case 'ERC-404': case 'ERC-404':
return generateListStub<'token_instance_holders'>(TOKEN_HOLDER_ERC_1155, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_holders'>(TOKEN_HOLDER_ERC_1155, 10, { next_page_params: pagination });
default: default:
return generateListStub<'token_instance_holders'>(TOKEN_HOLDER_ERC_20, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_holders'>(TOKEN_HOLDER_ERC_20, 10, { next_page_params: pagination });
} }
}; };
...@@ -140,26 +140,26 @@ export const TOKEN_TRANSFER_ERC_404: TokenTransfer = { ...@@ -140,26 +140,26 @@ export const TOKEN_TRANSFER_ERC_404: TokenTransfer = {
export const getTokenTransfersStub = (type?: TokenType, pagination: TokenTransferPagination | null = null): TokenTransferResponse => { export const getTokenTransfersStub = (type?: TokenType, pagination: TokenTransferPagination | null = null): TokenTransferResponse => {
switch (type) { switch (type) {
case 'ERC-721': case 'ERC-721':
return generateListStub<'token_transfers'>(TOKEN_TRANSFER_ERC_721, 50, { next_page_params: pagination }); return generateListStub<'general:token_transfers'>(TOKEN_TRANSFER_ERC_721, 50, { next_page_params: pagination });
case 'ERC-1155': case 'ERC-1155':
return generateListStub<'token_transfers'>(TOKEN_TRANSFER_ERC_1155, 50, { next_page_params: pagination }); return generateListStub<'general:token_transfers'>(TOKEN_TRANSFER_ERC_1155, 50, { next_page_params: pagination });
case 'ERC-404': case 'ERC-404':
return generateListStub<'token_transfers'>(TOKEN_TRANSFER_ERC_404, 50, { next_page_params: pagination }); return generateListStub<'general:token_transfers'>(TOKEN_TRANSFER_ERC_404, 50, { next_page_params: pagination });
default: default:
return generateListStub<'token_transfers'>(TOKEN_TRANSFER_ERC_20, 50, { next_page_params: pagination }); return generateListStub<'general:token_transfers'>(TOKEN_TRANSFER_ERC_20, 50, { next_page_params: pagination });
} }
}; };
export const getTokenInstanceTransfersStub = (type?: TokenType, pagination: TokenInstanceTransferPagination | null = null): TokenInstanceTransferResponse => { export const getTokenInstanceTransfersStub = (type?: TokenType, pagination: TokenInstanceTransferPagination | null = null): TokenInstanceTransferResponse => {
switch (type) { switch (type) {
case 'ERC-721': case 'ERC-721':
return generateListStub<'token_instance_transfers'>(TOKEN_TRANSFER_ERC_721, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_transfers'>(TOKEN_TRANSFER_ERC_721, 10, { next_page_params: pagination });
case 'ERC-1155': case 'ERC-1155':
return generateListStub<'token_instance_transfers'>(TOKEN_TRANSFER_ERC_1155, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_transfers'>(TOKEN_TRANSFER_ERC_1155, 10, { next_page_params: pagination });
case 'ERC-404': case 'ERC-404':
return generateListStub<'token_instance_transfers'>(TOKEN_TRANSFER_ERC_404, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_transfers'>(TOKEN_TRANSFER_ERC_404, 10, { next_page_params: pagination });
default: default:
return generateListStub<'token_instance_transfers'>(TOKEN_TRANSFER_ERC_20, 10, { next_page_params: pagination }); return generateListStub<'general:token_instance_transfers'>(TOKEN_TRANSFER_ERC_20, 10, { next_page_params: pagination });
} }
}; };
......
import type { ArrayElement } from 'types/utils'; import type { ArrayElement } from 'types/utils';
import type { PaginatedResources, PaginatedResponse, PaginatedResponseItems } from 'lib/api/resources'; import type { PaginatedResourceName, PaginatedResourceResponse, PaginatedResourceResponseItems } from 'lib/api/resources';
export function generateListStub<Resource extends PaginatedResources>( export function generateListStub<Resource extends PaginatedResourceName>(
stub: ArrayElement<PaginatedResponseItems<Resource>>, stub: ArrayElement<PaginatedResourceResponseItems<Resource>>,
num = 50, num = 50,
rest: Omit<PaginatedResponse<Resource>, 'items'>, rest: Omit<PaginatedResourceResponse<Resource>, 'items'>,
) { ) {
return { return {
items: Array(num).fill(stub), items: Array(num).fill(stub),
......
...@@ -37,11 +37,11 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P ...@@ -37,11 +37,11 @@ const AddressAccountHistory = ({ shouldRender = true, isQueryEnabled = true }: P
const [ filterValue, setFilterValue ] = React.useState<NovesHistoryFilterValue>(getFilterValue(router.query.filter)); const [ filterValue, setFilterValue ] = React.useState<NovesHistoryFilterValue>(getFilterValue(router.query.filter));
const { data, isError, pagination, isPlaceholderData } = useQueryWithPages({ const { data, isError, pagination, isPlaceholderData } = useQueryWithPages({
resourceName: 'noves_address_history', resourceName: 'general:noves_address_history',
pathParams: { address: currentAddress }, pathParams: { address: currentAddress },
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: generateListStub<'noves_address_history'>(NOVES_TRANSLATE, 10, { hasNextPage: false, pageNumber: 1, pageSize: 10 }), placeholderData: generateListStub<'general:noves_address_history'>(NOVES_TRANSLATE, 10, { hasNextPage: false, pageNumber: 1, pageSize: 10 }),
}, },
}); });
......
...@@ -41,11 +41,11 @@ const AddressBlocksValidated = ({ shouldRender = true, isQueryEnabled = true }: ...@@ -41,11 +41,11 @@ const AddressBlocksValidated = ({ shouldRender = true, isQueryEnabled = true }:
const addressHash = String(router.query.hash); const addressHash = String(router.query.hash);
const query = useQueryWithPages({ const query = useQueryWithPages({
resourceName: 'address_blocks_validated', resourceName: 'general:address_blocks_validated',
pathParams: { hash: addressHash }, pathParams: { hash: addressHash },
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: generateListStub<'address_blocks_validated'>( placeholderData: generateListStub<'general:address_blocks_validated'>(
BLOCK, BLOCK,
50, 50,
{ {
...@@ -66,7 +66,7 @@ const AddressBlocksValidated = ({ shouldRender = true, isQueryEnabled = true }: ...@@ -66,7 +66,7 @@ const AddressBlocksValidated = ({ shouldRender = true, isQueryEnabled = true }:
setSocketAlert(''); setSocketAlert('');
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('address_blocks_validated', { pathParams: { hash: addressHash } }), getResourceKey('general:address_blocks_validated', { pathParams: { hash: addressHash } }),
(prevData: AddressBlocksValidatedResponse | undefined) => { (prevData: AddressBlocksValidatedResponse | undefined) => {
if (!prevData) { if (!prevData) {
return; return;
......
...@@ -13,8 +13,8 @@ const hooksConfig = { ...@@ -13,8 +13,8 @@ const hooksConfig = {
}; };
test('base view +@dark-mode', async({ render, page, mockApiResponse }) => { test('base view +@dark-mode', async({ render, page, mockApiResponse }) => {
await mockApiResponse('address_coin_balance', balanceHistoryMock.baseResponse, { pathParams: { hash: addressHash } }); await mockApiResponse('general:address_coin_balance', balanceHistoryMock.baseResponse, { pathParams: { hash: addressHash } });
await mockApiResponse('address_coin_balance_chart', balanceHistoryMock.chartResponse, { pathParams: { hash: addressHash } }); await mockApiResponse('general:address_coin_balance_chart', balanceHistoryMock.chartResponse, { pathParams: { hash: addressHash } });
const component = await render(<AddressCoinBalance/>, { hooksConfig }); const component = await render(<AddressCoinBalance/>, { hooksConfig });
await page.waitForFunction(() => { await page.waitForFunction(() => {
return document.querySelector('path[data-name="chart-Balances-small"]')?.getAttribute('opacity') === '1'; return document.querySelector('path[data-name="chart-Balances-small"]')?.getAttribute('opacity') === '1';
...@@ -28,8 +28,8 @@ test.describe('mobile', () => { ...@@ -28,8 +28,8 @@ test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('base view', async({ render, page, mockApiResponse }) => { test('base view', async({ render, page, mockApiResponse }) => {
await mockApiResponse('address_coin_balance', balanceHistoryMock.baseResponse, { pathParams: { hash: addressHash } }); await mockApiResponse('general:address_coin_balance', balanceHistoryMock.baseResponse, { pathParams: { hash: addressHash } });
await mockApiResponse('address_coin_balance_chart', balanceHistoryMock.chartResponse, { pathParams: { hash: addressHash } }); await mockApiResponse('general:address_coin_balance_chart', balanceHistoryMock.chartResponse, { pathParams: { hash: addressHash } });
const component = await render(<AddressCoinBalance/>, { hooksConfig }); const component = await render(<AddressCoinBalance/>, { hooksConfig });
await page.waitForFunction(() => { await page.waitForFunction(() => {
return document.querySelector('path[data-name="chart-Balances-small"]')?.getAttribute('opacity') === '1'; return document.querySelector('path[data-name="chart-Balances-small"]')?.getAttribute('opacity') === '1';
......
...@@ -33,12 +33,12 @@ const AddressCoinBalance = ({ shouldRender = true, isQueryEnabled = true }: Prop ...@@ -33,12 +33,12 @@ const AddressCoinBalance = ({ shouldRender = true, isQueryEnabled = true }: Prop
const addressHash = getQueryParamString(router.query.hash); const addressHash = getQueryParamString(router.query.hash);
const coinBalanceQuery = useQueryWithPages({ const coinBalanceQuery = useQueryWithPages({
resourceName: 'address_coin_balance', resourceName: 'general:address_coin_balance',
pathParams: { hash: addressHash }, pathParams: { hash: addressHash },
scrollRef, scrollRef,
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: generateListStub<'address_coin_balance'>( placeholderData: generateListStub<'general:address_coin_balance'>(
ADDRESS_COIN_BALANCE, ADDRESS_COIN_BALANCE,
50, 50,
{ {
...@@ -59,7 +59,7 @@ const AddressCoinBalance = ({ shouldRender = true, isQueryEnabled = true }: Prop ...@@ -59,7 +59,7 @@ const AddressCoinBalance = ({ shouldRender = true, isQueryEnabled = true }: Prop
setSocketAlert(false); setSocketAlert(false);
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('address_coin_balance', { pathParams: { hash: addressHash } }), getResourceKey('general:address_coin_balance', { pathParams: { hash: addressHash } }),
(prevData: AddressCoinBalanceHistoryResponse | undefined) => { (prevData: AddressCoinBalanceHistoryResponse | undefined) => {
if (!prevData) { if (!prevData) {
return; return;
......
...@@ -12,9 +12,9 @@ import AddressContract from './AddressContract.pwstory'; ...@@ -12,9 +12,9 @@ import AddressContract from './AddressContract.pwstory';
const hash = addressMock.contract.hash; const hash = addressMock.contract.hash;
test.beforeEach(async({ mockApiResponse }) => { test.beforeEach(async({ mockApiResponse }) => {
await mockApiResponse('address', addressMock.contract, { pathParams: { hash } }); await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash } });
await mockApiResponse( await mockApiResponse(
'contract', 'general:contract',
{ ...contractInfoMock.verified, abi: [ ...contractMethodsMock.read, ...contractMethodsMock.write ] }, { ...contractInfoMock.verified, abi: [ ...contractMethodsMock.read, ...contractMethodsMock.write ] },
{ pathParams: { hash } }, { pathParams: { hash } },
); );
......
...@@ -10,7 +10,7 @@ import AddressContract from './AddressContract'; ...@@ -10,7 +10,7 @@ import AddressContract from './AddressContract';
const AddressContractPwStory = () => { const AddressContractPwStory = () => {
const router = useRouter(); const router = useRouter();
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const addressQuery = useApiQuery('address', { pathParams: { hash } }); const addressQuery = useApiQuery('general:address', { pathParams: { hash } });
const { tabs } = useContractTabs(addressQuery.data, false); const { tabs } = useContractTabs(addressQuery.data, false);
return <AddressContract tabs={ tabs } shouldRender={ true } isLoading={ false }/>; return <AddressContract tabs={ tabs } shouldRender={ true } isLoading={ false }/>;
}; };
......
...@@ -21,8 +21,8 @@ test.describe('mobile', () => { ...@@ -21,8 +21,8 @@ test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('contract', async({ render, mockApiResponse, page }) => { test('contract', async({ render, mockApiResponse, page }) => {
await mockApiResponse('address', addressMock.contract, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forContract, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forContract, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.contract } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.contract } as AddressQuery}/>, { hooksConfig });
...@@ -33,8 +33,8 @@ test.describe('mobile', () => { ...@@ -33,8 +33,8 @@ test.describe('mobile', () => {
}); });
test('validator', async({ render, page, mockApiResponse }) => { test('validator', async({ render, page, mockApiResponse }) => {
await mockApiResponse('address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.validator } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.validator } as AddressQuery}/>, { hooksConfig });
...@@ -45,8 +45,8 @@ test.describe('mobile', () => { ...@@ -45,8 +45,8 @@ test.describe('mobile', () => {
}); });
test('filecoin', async({ render, mockApiResponse, page }) => { test('filecoin', async({ render, mockApiResponse, page }) => {
await mockApiResponse('address', addressMock.filecoin, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.filecoin, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.filecoin } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.filecoin } as AddressQuery}/>, { hooksConfig });
...@@ -58,8 +58,8 @@ test.describe('mobile', () => { ...@@ -58,8 +58,8 @@ test.describe('mobile', () => {
}); });
test('contract', async({ render, page, mockApiResponse }) => { test('contract', async({ render, page, mockApiResponse }) => {
await mockApiResponse('address', addressMock.contract, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.contract, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forContract, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forContract, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.contract } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.contract } as AddressQuery}/>, { hooksConfig });
...@@ -71,12 +71,12 @@ test('contract', async({ render, page, mockApiResponse }) => { ...@@ -71,12 +71,12 @@ test('contract', async({ render, page, mockApiResponse }) => {
// there's an unexpected timeout occurred in this test // there's an unexpected timeout occurred in this test
test.fixme('token', async({ render, mockApiResponse, injectMetaMaskProvider, page }) => { test.fixme('token', async({ render, mockApiResponse, injectMetaMaskProvider, page }) => {
await mockApiResponse('address', addressMock.token, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.token, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forToken, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forToken, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_tokens', tokensMock.erc20List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' }, times: 1 }); await mockApiResponse('general:address_tokens', tokensMock.erc20List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' }, times: 1 });
await mockApiResponse('address_tokens', tokensMock.erc721List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' }, times: 1 }); await mockApiResponse('general:address_tokens', tokensMock.erc721List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' }, times: 1 });
await mockApiResponse('address_tokens', tokensMock.erc1155List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' }, times: 1 }); await mockApiResponse('general:address_tokens', tokensMock.erc1155List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' }, times: 1 });
await mockApiResponse('address_tokens', tokensMock.erc404List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' }, times: 1 }); await mockApiResponse('general:address_tokens', tokensMock.erc404List, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' }, times: 1 });
await injectMetaMaskProvider(); await injectMetaMaskProvider();
const component = await render( const component = await render(
...@@ -93,8 +93,8 @@ test.fixme('token', async({ render, mockApiResponse, injectMetaMaskProvider, pag ...@@ -93,8 +93,8 @@ test.fixme('token', async({ render, mockApiResponse, injectMetaMaskProvider, pag
}); });
test('validator', async({ render, mockApiResponse, page }) => { test('validator', async({ render, mockApiResponse, page }) => {
await mockApiResponse('address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.validator } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.validator } as AddressQuery}/>, { hooksConfig });
...@@ -105,8 +105,8 @@ test('validator', async({ render, mockApiResponse, page }) => { ...@@ -105,8 +105,8 @@ test('validator', async({ render, mockApiResponse, page }) => {
}); });
test('filecoin', async({ render, mockApiResponse, page }) => { test('filecoin', async({ render, mockApiResponse, page }) => {
await mockApiResponse('address', addressMock.filecoin, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.filecoin, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_counters', countersMock.forValidator, { pathParams: { hash: ADDRESS_HASH } });
const component = await render(<AddressDetails addressQuery={{ data: addressMock.filecoin } as AddressQuery}/>, { hooksConfig }); const component = await render(<AddressDetails addressQuery={{ data: addressMock.filecoin } as AddressQuery}/>, { hooksConfig });
......
...@@ -14,7 +14,7 @@ const hooksConfig = { ...@@ -14,7 +14,7 @@ const hooksConfig = {
}; };
test('base view +@mobile', async({ render, mockApiResponse }) => { test('base view +@mobile', async({ render, mockApiResponse }) => {
await mockApiResponse('address_epoch_rewards', epochRewards, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_epoch_rewards', epochRewards, { pathParams: { hash: ADDRESS_HASH } });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
<AddressEpochRewards/> <AddressEpochRewards/>
......
...@@ -27,13 +27,13 @@ const AddressEpochRewards = ({ shouldRender = true, isQueryEnabled = true }: Pro ...@@ -27,13 +27,13 @@ const AddressEpochRewards = ({ shouldRender = true, isQueryEnabled = true }: Pro
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const rewardsQuery = useQueryWithPages({ const rewardsQuery = useQueryWithPages({
resourceName: 'address_epoch_rewards', resourceName: 'general:address_epoch_rewards',
pathParams: { pathParams: {
hash, hash,
}, },
options: { options: {
enabled: isQueryEnabled && Boolean(hash), enabled: isQueryEnabled && Boolean(hash),
placeholderData: generateListStub<'address_epoch_rewards'>(EPOCH_REWARD_ITEM, 50, { next_page_params: { placeholderData: generateListStub<'general:address_epoch_rewards'>(EPOCH_REWARD_ITEM, 50, { next_page_params: {
amount: '1', amount: '1',
items_count: 50, items_count: 50,
type: 'voter', type: 'voter',
......
...@@ -15,7 +15,7 @@ const hooksConfig = { ...@@ -15,7 +15,7 @@ const hooksConfig = {
test('base view +@mobile', async({ render, mockApiResponse }) => { test('base view +@mobile', async({ render, mockApiResponse }) => {
test.slow(); test.slow();
await mockApiResponse('address_internal_txs', internalTxsMock.baseResponse, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address_internal_txs', internalTxsMock.baseResponse, { pathParams: { hash: ADDRESS_HASH } });
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
<AddressInternalTxs/> <AddressInternalTxs/>
......
...@@ -36,12 +36,12 @@ const AddressInternalTxs = ({ shouldRender = true, isQueryEnabled = true }: Prop ...@@ -36,12 +36,12 @@ const AddressInternalTxs = ({ shouldRender = true, isQueryEnabled = true }: Prop
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const { data, isPlaceholderData, isError, pagination, onFilterChange } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'address_internal_txs', resourceName: 'general:address_internal_txs',
pathParams: { hash }, pathParams: { hash },
filters: { filter: filterValue }, filters: { filter: filterValue },
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: generateListStub<'address_internal_txs'>( placeholderData: generateListStub<'general:address_internal_txs'>(
INTERNAL_TX, INTERNAL_TX,
50, 50,
{ {
......
...@@ -25,11 +25,11 @@ const AddressLogs = ({ shouldRender = true, isQueryEnabled = true }: Props) => { ...@@ -25,11 +25,11 @@ const AddressLogs = ({ shouldRender = true, isQueryEnabled = true }: Props) => {
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({ const { data, isPlaceholderData, isError, pagination } = useQueryWithPages({
resourceName: 'address_logs', resourceName: 'general:address_logs',
pathParams: { hash }, pathParams: { hash },
options: { options: {
enabled: isQueryEnabled, enabled: isQueryEnabled,
placeholderData: generateListStub<'address_logs'>(LOG, 3, { next_page_params: { placeholderData: generateListStub<'general:address_logs'>(LOG, 3, { next_page_params: {
block_number: 9005750, block_number: 9005750,
index: 42, index: 42,
items_count: 50, items_count: 50,
......
...@@ -28,7 +28,7 @@ const tokenTransfersWoPagination = { ...@@ -28,7 +28,7 @@ const tokenTransfersWoPagination = {
}; };
test('with pagination', async({ render, mockApiResponse }) => { test('with pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -42,7 +42,7 @@ test('with pagination', async({ render, mockApiResponse }) => { ...@@ -42,7 +42,7 @@ test('with pagination', async({ render, mockApiResponse }) => {
}); });
test('without pagination', async({ render, mockApiResponse }) => { test('without pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWoPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -59,7 +59,7 @@ test.describe('mobile', () => { ...@@ -59,7 +59,7 @@ test.describe('mobile', () => {
test.use({ viewport: devices['iPhone 13 Pro'].viewport }); test.use({ viewport: devices['iPhone 13 Pro'].viewport });
test('with pagination', async({ render, mockApiResponse }) => { test('with pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -73,7 +73,7 @@ test.describe('mobile', () => { ...@@ -73,7 +73,7 @@ test.describe('mobile', () => {
}); });
test('without pagination', async({ render, mockApiResponse }) => { test('without pagination', async({ render, mockApiResponse }) => {
await mockApiResponse('address_token_transfers', tokenTransfersWoPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWoPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -94,7 +94,7 @@ test.describe('socket', () => { ...@@ -94,7 +94,7 @@ test.describe('socket', () => {
query: { hash: CURRENT_ADDRESS }, query: { hash: CURRENT_ADDRESS },
}, },
}; };
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -127,7 +127,7 @@ test.describe('socket', () => { ...@@ -127,7 +127,7 @@ test.describe('socket', () => {
query: { hash: CURRENT_ADDRESS }, query: { hash: CURRENT_ADDRESS },
}, },
}; };
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: [] }, queryParams: { type: [] },
}); });
...@@ -163,7 +163,7 @@ test.describe('socket', () => { ...@@ -163,7 +163,7 @@ test.describe('socket', () => {
query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' }, query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' },
}, },
}; };
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: 'ERC-1155' }, queryParams: { type: 'ERC-1155' },
}); });
...@@ -197,7 +197,7 @@ test.describe('socket', () => { ...@@ -197,7 +197,7 @@ test.describe('socket', () => {
query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' }, query: { hash: CURRENT_ADDRESS, type: 'ERC-1155' },
}, },
}; };
await mockApiResponse('address_token_transfers', tokenTransfersWithPagination, { await mockApiResponse('general:address_token_transfers', tokenTransfersWithPagination, {
pathParams: { hash: CURRENT_ADDRESS }, pathParams: { hash: CURRENT_ADDRESS },
queryParams: { type: 'ERC-1155' }, queryParams: { type: 'ERC-1155' },
}); });
......
...@@ -84,7 +84,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -84,7 +84,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
); );
const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({ const { isError, isPlaceholderData, data, pagination, onFilterChange } = useQueryWithPages({
resourceName: 'address_token_transfers', resourceName: 'general:address_token_transfers',
pathParams: { hash: currentAddress }, pathParams: { hash: currentAddress },
filters, filters,
options: { options: {
...@@ -132,7 +132,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender = ...@@ -132,7 +132,7 @@ const AddressTokenTransfers = ({ overloadCount = OVERLOAD_COUNT, shouldRender =
if (newItems.length > 0) { if (newItems.length > 0) {
queryClient.setQueryData( queryClient.setQueryData(
getResourceKey('address_token_transfers', { pathParams: { hash: currentAddress }, queryParams: { ...filters } }), getResourceKey('general:address_token_transfers', { pathParams: { hash: currentAddress }, queryParams: { ...filters } }),
(prevData: AddressTokenTransferResponse | undefined) => { (prevData: AddressTokenTransferResponse | undefined) => {
if (!prevData) { if (!prevData) {
return; return;
......
...@@ -39,13 +39,13 @@ test.beforeEach(async({ mockApiResponse }) => { ...@@ -39,13 +39,13 @@ test.beforeEach(async({ mockApiResponse }) => {
next_page_params: nextPageParams, next_page_params: nextPageParams,
}; };
await mockApiResponse('address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } }); await mockApiResponse('general:address', addressMock.validator, { pathParams: { hash: ADDRESS_HASH } });
await mockApiResponse('address_tokens', response20, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } }); await mockApiResponse('general:address_tokens', response20, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } });
await mockApiResponse('address_tokens', response721, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' } }); await mockApiResponse('general:address_tokens', response721, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' } });
await mockApiResponse('address_tokens', response1155, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' } }); await mockApiResponse('general:address_tokens', response1155, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' } });
await mockApiResponse('address_tokens', response404, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' } }); await mockApiResponse('general:address_tokens', response404, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' } });
await mockApiResponse('address_nfts', tokensMock.nfts, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: [] } }); await mockApiResponse('general:address_nfts', tokensMock.nfts, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: [] } });
await mockApiResponse('address_collections', tokensMock.collections, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: [] } }); await mockApiResponse('general:address_collections', tokensMock.collections, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: [] } });
}); });
test('erc20 +@dark-mode', async({ render }) => { test('erc20 +@dark-mode', async({ render }) => {
...@@ -198,10 +198,18 @@ test.describe('update balances via socket', () => { ...@@ -198,10 +198,18 @@ test.describe('update balances via socket', () => {
next_page_params: null, next_page_params: null,
}; };
const erc20ApiUrl = await mockApiResponse('address_tokens', response20, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } }); const erc20ApiUrl = await mockApiResponse('general:address_tokens', response20, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-20' } });
const erc721ApiUrl = await mockApiResponse('address_tokens', response721, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' } }); const erc721ApiUrl = await mockApiResponse('general:address_tokens', response721, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-721' } });
const erc1155ApiUrl = await mockApiResponse('address_tokens', response1155, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' } }); const erc1155ApiUrl = await mockApiResponse(
const erc404ApiUrl = await mockApiResponse('address_tokens', response404, { pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' } }); 'general:address_tokens',
response1155,
{ pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-1155' } },
);
const erc404ApiUrl = await mockApiResponse(
'general:address_tokens',
response404,
{ pathParams: { hash: ADDRESS_HASH }, queryParams: { type: 'ERC-404' } },
);
const component = await render( const component = await render(
<Box pt={{ base: '134px', lg: 6 }}> <Box pt={{ base: '134px', lg: 6 }}>
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -21,11 +21,11 @@ const AddressUserOps = ({ scrollRef, shouldRender = true, isQueryEnabled = true ...@@ -21,11 +21,11 @@ const AddressUserOps = ({ scrollRef, shouldRender = true, isQueryEnabled = true
const hash = getQueryParamString(router.query.hash); const hash = getQueryParamString(router.query.hash);
const userOpsQuery = useQueryWithPages({ const userOpsQuery = useQueryWithPages({
resourceName: 'user_ops', resourceName: 'general:user_ops',
scrollRef, scrollRef,
options: { options: {
enabled: isQueryEnabled && Boolean(hash), enabled: isQueryEnabled && Boolean(hash),
placeholderData: generateListStub<'user_ops'>(USER_OPS_ITEM, 50, { next_page_params: { placeholderData: generateListStub<'general:user_ops'>(USER_OPS_ITEM, 50, { next_page_params: {
page_token: '10355938,0x5956a847d8089e254e02e5111cad6992b99ceb9e5c2dc4343fd53002834c4dc6', page_token: '10355938,0x5956a847d8089e254e02e5111cad6992b99ceb9e5c2dc4343fd53002834c4dc6',
page_size: 50, page_size: 50,
} }), } }),
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment