get-contracts-metadata.js 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. const fs = require('fs');
  2. const glob = require('glob');
  3. const match = require('micromatch');
  4. const path = require('path');
  5. const { findAll } = require('solidity-ast/utils');
  6. module.exports = function (
  7. pattern = 'contracts/**/*.sol',
  8. skipPatterns = ['contracts/mocks/**/*.sol'],
  9. artifacts = [],
  10. ) {
  11. // Use available hardhat artifacts. They reliably identify pragmas and the contracts, libraries and interfaces
  12. // definitions with minimal IO operations.
  13. const metadata = Object.fromEntries(
  14. artifacts.flatMap(artifact => {
  15. const { output: solcOutput } = require(path.resolve(__dirname, '..', artifact));
  16. return Object.keys(solcOutput.contracts)
  17. .filter(source => match.all(source, pattern) && !match.any(source, skipPatterns))
  18. .map(source => [
  19. source,
  20. {
  21. pragma: Array.from(findAll('PragmaDirective', solcOutput.sources[source].ast))
  22. .find(({ literals }) => literals.at(0) == 'solidity')
  23. .literals.slice(1)
  24. .join(''),
  25. sources: Array.from(findAll('ImportDirective', solcOutput.sources[source].ast)).map(
  26. ({ absolutePath }) => absolutePath,
  27. ),
  28. interface: Array.from(findAll('ContractDefinition', solcOutput.sources[source].ast)).every(
  29. ({ contractKind }) => contractKind === 'interface',
  30. ),
  31. },
  32. ]);
  33. }),
  34. );
  35. // Artifacts are missing files that only include imports. We have a few of these in contracts/interfaces
  36. // We add the missing metadata entries using the foundry artifacts
  37. glob
  38. .sync(pattern)
  39. .filter(file => !match.any(file, skipPatterns) && !Object.hasOwn(metadata, file))
  40. .forEach(file => {
  41. const entries = glob.sync(`out/${path.basename(file)}/*`);
  42. metadata[file] = {
  43. pragma: fs.readFileSync(file, 'utf-8').match(/pragma solidity (?<pragma>[<>=^]*[0-9]+\.[0-9]+\.[0-9]+);/)
  44. ?.groups.pragma,
  45. sources: entries
  46. .flatMap(entry => Object.keys(JSON.parse(fs.readFileSync(entry)).metadata.sources))
  47. .filter(source => source !== file && match.all(source, pattern) && !match.any(source, skipPatterns)),
  48. interface: entries.every(entry => path.basename(entry).match(/^I[A-Z]/)),
  49. };
  50. });
  51. return metadata;
  52. };