import * as fs from 'fs' import * as path from 'path' import { getAddress, type Address } from 'viem' // Type definitions for DVN metadata interface DVNInfo { version: number canonicalName: string id: string deprecated?: boolean lzReadCompatible?: boolean } interface ChainMetadata { created: string updated: string tableName: string environment: string chainName: string dvns?: { [dvnAddress: string]: DVNInfo } } interface DVNMetadataResponse { [chainName: string]: ChainMetadata } interface DVNConfig { [chain: string]: { [dvnName: string]: Address } } function compareAlphabetically(left: string, right: string): number { return left.localeCompare(right) } // Type guard to validate DVN metadata structure function isDVNMetadataResponse(data: unknown): data is DVNMetadataResponse { return typeof data === 'object' && data !== null } async function extractDVNs(): Promise { const parentDir = path.join(__dirname, '../..') const deploymentsDir = path.join(parentDir, 'deployments') const outputPath = path.join(parentDir, 'configs', 'global', 'dvns.json') // Folder name mappings for LayerZero (same as in extractChainInfo.ts) const lzNameMap: Record = { kaia: 'klaytn', cronos: 'cronosevm', bnb: 'bsc', } // Fetch DVN metadata console.log('Fetching DVN metadata from LayerZero...') const response = await fetch( 'https://metadata.layerzero-api.com/v1/metadata/dvns' ) const metadata = await response.json() if (!isDVNMetadataResponse(metadata)) { throw new Error('Invalid DVN metadata response format') } // Get all chains from deployments directory const allChains = fs .readdirSync(deploymentsDir) .filter((f) => fs.statSync(path.join(deploymentsDir, f)).isDirectory()) console.log(`Found ${allChains.length} chains in deployments directory`) // Create mapping of LayerZero chain names to our chain names const lzToOurChainMap: Record = {} for (const chain of allChains) { const lzChainName = lzNameMap[chain] || chain lzToOurChainMap[lzChainName] = chain } // Process DVN data - collect DVN coverage across chains const dvnCoverage: Map> = new Map() const activeDvnAddresses: Map>> = new Map() for (const [lzChainName, chainData] of Object.entries(metadata)) { const ourChainName = lzToOurChainMap[lzChainName] if (!ourChainName || !chainData.dvns) { continue } for (const [dvnAddress, dvnInfo] of Object.entries(chainData.dvns)) { // Skip deprecated or lzRead-compatible DVNs, which are not suitable for standard pathway config. if (dvnInfo.deprecated || dvnInfo.lzReadCompatible) { continue } // Use canonicalName as the DVN identifier // Imagine putting spaces in JSON keys const dvnName = dvnInfo.canonicalName.replace(/ /g, '_') if (!activeDvnAddresses.has(dvnName)) { activeDvnAddresses.set(dvnName, new Map()) } if (!activeDvnAddresses.get(dvnName)!.has(ourChainName)) { activeDvnAddresses.get(dvnName)!.set(ourChainName, new Set()) } activeDvnAddresses.get(dvnName)!.get(ourChainName)!.add( getAddress(dvnAddress) ) if (!dvnCoverage.has(dvnName)) { dvnCoverage.set(dvnName, new Map()) } dvnCoverage.get(dvnName)!.set(ourChainName, dvnAddress) } } // Filter DVNs that are present on all required chains and build output const dvnsByChain: DVNConfig = {} const universalDVNs: string[] = [] const checkedUniversalDVNs: string[] = [] // Initialize chain objects for (const chain of allChains) { dvnsByChain[chain] = {} } // Add DVNs that exist on all chains for (const [dvnName, chainMap] of dvnCoverage) { if (allChains.every((chain) => chainMap.has(chain))) { for (const chain of allChains) { const addresses = [ ...(activeDvnAddresses.get(dvnName)?.get(chain) ?? new Set()), ] if (addresses.length > 1) { throw new Error( `Universal DVN ${dvnName} has multiple active addresses on ${chain}: ${addresses.join( ', ' )}` ) } } checkedUniversalDVNs.push(dvnName) universalDVNs.push(dvnName) for (const chain of allChains) { dvnsByChain[chain][dvnName] = getAddress(chainMap.get(chain)!) } } } const sortedCheckedUniversalDVNs = [...checkedUniversalDVNs].sort( compareAlphabetically ) const sortedUniversalDVNs = [...universalDVNs].sort(compareAlphabetically) console.log(`Found ${universalDVNs.length} DVNs with full chain coverage`) console.log( `Verified unique active addresses for universal DVNs: ${sortedCheckedUniversalDVNs.join( ', ' )}` ) // Save result fs.mkdirSync(path.dirname(outputPath), { recursive: true }) fs.writeFileSync(outputPath, JSON.stringify(dvnsByChain, null, 2)) console.log( `Saved DVN data for ${ Object.keys(dvnsByChain).length } chains to ${outputPath}` ) // Log summary console.log(`\nUniversal DVNs included: ${sortedUniversalDVNs.join(', ')}`) } extractDVNs().catch((error) => { console.error(error) process.exitCode = 1 })