UNPKG

@hyperlane-xyz/sdk

Version:

The official SDK for the Hyperlane Network

112 lines 4.55 kB
import { getAbiItem, parseEventLogs, toEventSelector } from 'viem'; import { IXERC20Lockbox__factory } from '@hyperlane-xyz/core'; import { rootLogger } from '@hyperlane-xyz/utils'; import { getContractDeploymentTransaction, getLogsFromEtherscanLikeExplorerAPI, } from '../block-explorer/etherscan.js'; import { viemLogFromGetEventLogsResponse } from '../rpc/evm/utils.js'; import { XERC20Type } from './types.js'; const minimalXERC20VSABI = [ { anonymous: false, inputs: [ { indexed: true, internalType: 'address', name: 'bridge', type: 'address', }, { indexed: false, internalType: 'uint112', name: 'bufferCap', type: 'uint112', }, { indexed: false, internalType: 'uint128', name: 'rateLimitPerSecond', type: 'uint128', }, ], name: 'ConfigurationChanged', type: 'event', }, ]; const CONFIGURATION_CHANGED_EVENT_SELECTOR = toEventSelector(getAbiItem({ abi: minimalXERC20VSABI, name: 'ConfigurationChanged', })); export async function getExtraLockBoxConfigs({ xERC20Address, chain, multiProvider, logger = rootLogger, }) { const explorer = multiProvider.tryGetEvmExplorerMetadata(chain); if (!explorer) { logger.warn(`No block explorer was configured correctly, skipping lockbox derivation on chain ${chain}`); return []; } const logs = await getConfigurationChangedLogsFromExplorerApi({ chain, multiProvider, xERC20Address, explorerUrl: explorer.apiUrl, apiKey: explorer.apiKey, }); const viemLogs = logs.map(viemLogFromGetEventLogsResponse); return getLockboxesFromLogs(viemLogs, multiProvider.getProvider(chain), chain, logger); } async function getConfigurationChangedLogsFromExplorerApi({ xERC20Address, chain, multiProvider, explorerUrl, apiKey, }) { const contractDeploymentTx = await getContractDeploymentTransaction({ apiUrl: explorerUrl, apiKey }, { contractAddress: xERC20Address }); const provider = multiProvider.getProvider(chain); const [currentBlockNumber, deploymentTransactionReceipt] = await Promise.all([ provider.getBlockNumber(), provider.getTransactionReceipt(contractDeploymentTx.txHash), ]); return getLogsFromEtherscanLikeExplorerAPI({ apiUrl: explorerUrl, apiKey }, { address: xERC20Address, fromBlock: deploymentTransactionReceipt.blockNumber, toBlock: currentBlockNumber, topic0: CONFIGURATION_CHANGED_EVENT_SELECTOR, }); } async function getLockboxesFromLogs(logs, provider, chain, logger) { const parsedLogs = parseEventLogs({ abi: minimalXERC20VSABI, eventName: 'ConfigurationChanged', logs, }); // A bridge might appear more than once in the event logs, we are only // interested in the most recent one for each bridge so we deduplicate // entries here const dedupedBridges = parsedLogs.reduce((acc, log) => { const bridgeAddress = log.args.bridge; const isMostRecentLogForBridge = log.blockNumber > (acc[bridgeAddress]?.blockNumber ?? 0n); if (isMostRecentLogForBridge) { acc[bridgeAddress] = log; } return acc; }, {}); const lockboxPromises = Object.values(dedupedBridges) // Removing bridges where the limits are set to 0 because it is equivalent of being deactivated .filter((log) => log.args.bufferCap !== 0n && log.args.rateLimitPerSecond !== 0n) .map(async (log) => { try { const maybeXERC20Lockbox = IXERC20Lockbox__factory.connect(log.args.bridge, provider); await maybeXERC20Lockbox.callStatic.XERC20(); return log; } catch { logger.debug(`Contract at address ${log.args.bridge} on chain ${chain} is not a XERC20Lockbox contract.`); return undefined; } }); const lockboxes = await Promise.all(lockboxPromises); return lockboxes .filter((log) => log !== undefined) .map((log) => log) .map((log) => ({ lockbox: log.args.bridge, limits: { type: XERC20Type.Velo, bufferCap: log.args.bufferCap.toString(), rateLimitPerSecond: log.args.rateLimitPerSecond.toString(), }, })); } //# sourceMappingURL=xerc20.js.map