@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
99 lines • 3.76 kB
JavaScript
import { ProtocolType } from '@hyperlane-xyz/utils';
import { ExplorerFamily } from './chainMetadataTypes.js';
/**
* Converts Etherscan-like API URLs to the new V2 format
* @param apiUrl The original API URL
* @param chainId The chain ID to use for the V2 API
* @returns The converted V2 API URL
*/
// https://docs.etherscan.io/etherscan-v2/v2-quickstart
function convertToEtherscanV2Url(apiUrl, chainId, family) {
// Only convert if it's an Etherscan family explorer
if (family === ExplorerFamily.Etherscan) {
// Convert to Etherscan V2 format
const etherscanV2Url = new URL('https://api.etherscan.io/v2/api');
// Add chainId parameter if provided
if (chainId) {
etherscanV2Url.searchParams.set('chainid', chainId.toString());
}
return etherscanV2Url.toString();
}
// Return original URL if not an Etherscan family
return apiUrl;
}
export function getExplorerBaseUrl(metadata, index = 0) {
if (!metadata?.blockExplorers?.length)
return null;
const url = new URL(metadata.blockExplorers[index].url);
return url.toString();
}
export function getExplorerApi(metadata, index = 0) {
const { protocol, blockExplorers } = metadata;
// TODO solana + cosmos support here as needed
if (protocol !== ProtocolType.Ethereum)
return null;
if (!blockExplorers?.length || !blockExplorers[index].apiUrl)
return null;
// Convert to V2 format if it's an Etherscan-like API
const chainId = typeof metadata.chainId === 'string'
? parseInt(metadata.chainId)
: metadata.chainId;
const convertedApiUrl = convertToEtherscanV2Url(blockExplorers[index].apiUrl, chainId, blockExplorers[index].family);
return {
apiUrl: convertedApiUrl,
apiKey: blockExplorers[index].apiKey,
family: blockExplorers[index].family,
};
}
export function getExplorerApiUrl(metadata, index = 0) {
const explorer = getExplorerApi(metadata, index);
if (!explorer)
return null;
const { apiUrl, apiKey } = explorer;
if (!apiKey)
return apiUrl;
const url = new URL(apiUrl);
url.searchParams.set('apikey', apiKey);
return url.toString();
}
export function getExplorerTxUrl(metadata, hash) {
const baseUrl = getExplorerBaseUrl(metadata);
if (!baseUrl)
return null;
const chainName = metadata.name;
// TODO consider move handling of these chain/protocol specific quirks to ChainMetadata
const urlPathStub = ['nautilus', 'proteustestnet'].includes(chainName)
? 'transaction'
: 'tx';
return appendToPath(baseUrl, `${urlPathStub}/${hash}`).toString();
}
export function getExplorerAddressUrl(metadata, address) {
const baseUrl = getExplorerBaseUrl(metadata);
if (!baseUrl)
return null;
const urlPathStub = getExplorerAddressPathStub(metadata);
if (!urlPathStub)
return null;
return appendToPath(baseUrl, `${urlPathStub}/${address}`).toString();
}
function appendToPath(baseUrl, pathExtension) {
const base = new URL(baseUrl);
let currentPath = base.pathname;
if (currentPath.endsWith('/'))
currentPath = currentPath.slice(0, -1);
const newPath = `${currentPath}/${pathExtension}`;
const newUrl = new URL(newPath, base);
newUrl.search = base.searchParams.toString();
return newUrl;
}
function getExplorerAddressPathStub(metadata, index = 0) {
if (!metadata?.blockExplorers?.[index])
return null;
const blockExplorer = metadata.blockExplorers[index];
if (!blockExplorer.family)
return null;
return blockExplorer.family === ExplorerFamily.Voyager
? 'contract'
: 'address';
}
//# sourceMappingURL=blockExplorer.js.map