github.com/ethereum-optimism/optimism@v1.7.2/packages/contracts-bedrock/scripts/autogen/generate-snapshots.ts (about) 1 import fs from 'fs' 2 import path from 'path' 3 4 const root = path.join(__dirname, '..', '..') 5 const outdir = process.argv[2] || path.join(root, 'snapshots') 6 const forgeArtifactsDir = path.join(root, 'forge-artifacts') 7 8 const getAllContractsSources = (): Array<string> => { 9 const paths = [] 10 const readFilesRecursively = (dir: string) => { 11 const files = fs.readdirSync(dir) 12 13 for (const file of files) { 14 const filePath = path.join(dir, file) 15 const fileStat = fs.statSync(filePath) 16 17 if (fileStat.isDirectory()) { 18 readFilesRecursively(filePath) 19 } else { 20 paths.push(filePath) 21 } 22 } 23 } 24 readFilesRecursively(path.join(root, 'src')) 25 26 return paths 27 .filter((x) => x.endsWith('.sol')) 28 .map((p: string) => path.basename(p)) 29 .sort() 30 } 31 32 type ForgeArtifact = { 33 abi: object 34 ast: { 35 nodeType: string 36 nodes: any[] 37 } 38 storageLayout: { 39 storage: [{ type: string; label: string; offset: number; slot: number }] 40 types: { [key: string]: { label: string; numberOfBytes: number } } 41 } 42 bytecode: { 43 object: string 44 } 45 } 46 47 type AbiSpecStorageLayoutEntry = { 48 label: string 49 slot: number 50 offset: number 51 bytes: number 52 type: string 53 } 54 const sortKeys = (obj: any) => { 55 if (typeof obj !== 'object' || obj === null) { 56 return obj 57 } 58 return Object.keys(obj) 59 .sort() 60 .reduce( 61 (acc, key) => { 62 acc[key] = sortKeys(obj[key]) 63 return acc 64 }, 65 Array.isArray(obj) ? [] : {} 66 ) 67 } 68 69 // ContractName.0.9.8.json -> ContractName.sol 70 // ContractName.json -> ContractName.sol 71 const parseArtifactName = (artifactVersionFile: string): string => { 72 const match = artifactVersionFile.match(/(.*?)\.([0-9]+\.[0-9]+\.[0-9]+)?/) 73 if (!match) { 74 throw new Error(`Invalid artifact file name: ${artifactVersionFile}`) 75 } 76 return match[1] 77 } 78 79 const main = async () => { 80 console.log(`writing abi and storage layout snapshots to ${outdir}`) 81 82 const storageLayoutDir = path.join(outdir, 'storageLayout') 83 const abiDir = path.join(outdir, 'abi') 84 fs.mkdirSync(storageLayoutDir, { recursive: true }) 85 fs.mkdirSync(abiDir, { recursive: true }) 86 87 const contractSources = getAllContractsSources() 88 const knownAbis = {} 89 90 for (const contractFile of contractSources) { 91 const contractArtifacts = path.join(forgeArtifactsDir, contractFile) 92 for (const name of fs.readdirSync(contractArtifacts)) { 93 const data = fs.readFileSync(path.join(contractArtifacts, name)) 94 const artifact: ForgeArtifact = JSON.parse(data.toString()) 95 96 const contractName = parseArtifactName(name) 97 98 // HACK: This is a hack to ignore libraries and abstract contracts. Not robust against changes to solc's internal ast repr 99 const isContract = artifact.ast.nodes.some((node: any) => { 100 return ( 101 node.nodeType === 'ContractDefinition' && 102 node.name === contractName && 103 node.contractKind === 'contract' && 104 (node.abstract === undefined || // solc < 0.6 doesn't have explicit abstract contracts 105 node.abstract === false) 106 ) 107 }) 108 if (!isContract) { 109 console.log(`ignoring library/interface ${contractName}`) 110 continue 111 } 112 113 const storageLayout: AbiSpecStorageLayoutEntry[] = [] 114 for (const storageEntry of artifact.storageLayout.storage) { 115 // convert ast-based type to solidity type 116 const typ = artifact.storageLayout.types[storageEntry.type] 117 if (typ === undefined) { 118 throw new Error( 119 `undefined type for ${contractName}:${storageEntry.label}` 120 ) 121 } 122 storageLayout.push({ 123 label: storageEntry.label, 124 bytes: typ.numberOfBytes, 125 offset: storageEntry.offset, 126 slot: storageEntry.slot, 127 type: typ.label, 128 }) 129 } 130 131 if (knownAbis[contractName] === undefined) { 132 knownAbis[contractName] = artifact.abi 133 } else if ( 134 JSON.stringify(knownAbis[contractName]) !== JSON.stringify(artifact.abi) 135 ) { 136 throw Error( 137 `detected multiple artifact versions with different ABIs for ${contractFile}` 138 ) 139 } else { 140 console.log(`detected multiple artifacts for ${contractName}`) 141 } 142 143 // Sort snapshots for easier manual inspection 144 fs.writeFileSync( 145 `${abiDir}/${contractName}.json`, 146 JSON.stringify(sortKeys(artifact.abi), null, 2) 147 ) 148 fs.writeFileSync( 149 `${storageLayoutDir}/${contractName}.json`, 150 JSON.stringify(sortKeys(storageLayout), null, 2) 151 ) 152 } 153 } 154 } 155 156 main()