@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
112 lines • 4.55 kB
JavaScript
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