UNPKG

zonder

Version:

Ergonomic multi-chain indexing framework with dual runtime support for Ponder and Envio.

122 lines (121 loc) 5.55 kB
import { createPublicClient, http, } from 'viem'; import { safeWriteFileSync } from '../utils/safeWrite.js'; import { findDeploymentBlock } from './findDeploymentBlock.js'; function createClientForChain(chain) { const chainId = chain.id; const envVarName = `PONDER_RPC_URL_${chainId}`; const rpcUrl = process.env[envVarName]; if (!rpcUrl) { throw new Error(`Environment variable ${envVarName} is not set for chain ${chain.name}`); } return createPublicClient({ chain, transport: http(rpcUrl), }); } function hasSpecificStartBlock(config, chainName, contractName) { if (!config.startBlocks) return false; const chainStartBlocks = config.startBlocks[chainName]; if (!chainStartBlocks) return false; // Check if this contract has a specific start block (not just using default) return chainStartBlocks[contractName] !== undefined; } function writeDeploymentBlocks(results, overwrite = false) { try { safeWriteFileSync('start-blocks.json', JSON.stringify(results, null, 2), { overwrite }); } catch (error) { console.error('Failed to write start-blocks.json:', error); } } export async function findAllDeploymentBlocks(config, overwrite = false) { const results = {}; // Pre-populate with existing start blocks from config if (config.startBlocks) { for (const [chainName, chainStartBlocks] of Object.entries(config.startBlocks)) { if (chainStartBlocks) { results[chainName] = {}; for (const [contractName, blockNumber] of Object.entries(chainStartBlocks)) { if (contractName !== 'default' && typeof blockNumber === 'number') { results[chainName][contractName] = blockNumber; } } } } } // Initialize start-blocks.json with existing blocks writeDeploymentBlocks(results, overwrite); // Get all chain names from the config const chainNames = Object.keys(config.addresses); console.log(`Processing ${chainNames.length} chains: ${chainNames.join(', ')}`); for (const chainName of chainNames) { console.log(`\n${String(chainName)}:`); if (!results[String(chainName)]) { results[String(chainName)] = {}; } try { const chain = config.chains[chainName]; if (!chain) { console.error(`Chain ${String(chainName)} not found in config`); continue; } const client = createClientForChain(chain); const addresses = config.addresses[chainName]; if (!addresses) { console.log(` No addresses configured for ${String(chainName)}`); continue; } const addressEntries = Object.entries(addresses); const latestBlock = await client.getBlockNumber(); for (const [contractName, address] of addressEntries) { if (!address) { continue; } // Skip if this contract already has a specific start block configured if (hasSpecificStartBlock(config, chainName, contractName)) { console.log(` ${contractName}: already configured, skipping`); continue; } // Handle both single address and array of addresses const addressesToProcess = Array.isArray(address) ? address : [address]; process.stdout.write(` ${contractName}: `); for (let i = 0; i < addressesToProcess.length; i++) { const currentAddress = addressesToProcess[i]; try { const deploymentBlock = await findDeploymentBlock(client, currentAddress, latestBlock); if (deploymentBlock !== null) { const blockNumber = Number(deploymentBlock); // For arrays, use the earliest deployment block const chainResults = results[String(chainName)]; if (chainResults && (chainResults[contractName] === undefined || blockNumber < chainResults[contractName])) { chainResults[contractName] = blockNumber; // Write to file immediately after discovering each block writeDeploymentBlocks(results, overwrite); } console.log(blockNumber); break; // Found deployment block, no need to check other addresses } } catch (error) { // Silent error handling, continue to next address } // Small delay to avoid rate limiting await new Promise((resolve) => setTimeout(resolve, 100)); } // If no deployment block found for any address const chainResults = results[String(chainName)]; if (chainResults && !chainResults[contractName]) { console.log('not found'); } } } catch (error) { console.error(`Failed to process ${String(chainName)}: ${error}`); } } return results; }