#!/usr/bin/env node const path = require('path'); const graphlib = require('graphlib'); const { findAll } = require('solidity-ast/utils'); const { _: artifacts } = require('yargs').argv; for (const artifact of artifacts) { const { output: solcOutput } = require(path.resolve(__dirname, '../..', artifact)); const graph = new graphlib.Graph({ directed: true }); const names = {}; const linearized = []; for (const source in solcOutput.contracts) { if (source.includes('/mocks/')) { continue; } for (const contractDef of findAll('ContractDefinition', solcOutput.sources[source].ast)) { names[contractDef.id] = contractDef.name; linearized.push(contractDef.linearizedBaseContracts); contractDef.linearizedBaseContracts.forEach((c1, i, contracts) => contracts.slice(i + 1).forEach(c2 => { graph.setEdge(c1, c2); }), ); } } /// graphlib.alg.findCycles will not find minimal cycles. /// We are only interested int cycles of lengths 2 (needs proof) graph.nodes().forEach((x, i, nodes) => nodes .slice(i + 1) .filter(y => graph.hasEdge(x, y) && graph.hasEdge(y, x)) .forEach(y => { console.log(`Conflict between ${names[x]} and ${names[y]} detected in the following dependency chains:`); linearized .filter(chain => chain.includes(parseInt(x)) && chain.includes(parseInt(y))) .forEach(chain => { const comp = chain.indexOf(parseInt(x)) < chain.indexOf(parseInt(y)) ? '>' : '<'; console.log(`- ${names[x]} ${comp} ${names[y]} in ${names[chain.find(Boolean)]}`); // console.log(`- ${names[x]} ${comp} ${names[y]}: ${chain.reverse().map(id => names[id]).join(', ')}`); }); process.exitCode = 1; }), ); } if (!process.exitCode) { console.log('Contract ordering is consistent.'); }