forked from boringcrypto/hardhat-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hardhat-flat.js
112 lines (89 loc) · 3.82 KB
/
hardhat-flat.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
const { task, subtask, types } = require("hardhat/config");
const fs = require("fs")
function getSortedFiles(dependenciesGraph) {
const tsort = require("tsort")
const graph = tsort()
const filesMap = {}
const resolvedFiles = dependenciesGraph.getResolvedFiles()
resolvedFiles.forEach((f) => (filesMap[f.sourceName] = f))
for (const [from, deps] of dependenciesGraph.entries()) {
for (const to of deps) {
graph.add(to.sourceName, from.sourceName)
}
}
const topologicalSortedNames = graph.sort()
// If an entry has no dependency it won't be included in the graph, so we
// add them and then dedup the array
const withEntries = topologicalSortedNames.concat(resolvedFiles.map((f) => f.sourceName))
const sortedNames = [...new Set(withEntries)]
return sortedNames.map((n) => filesMap[n])
}
function getFileWithoutImports(resolvedFile) {
const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+)[\s\S]*?;\s*$/gm
return resolvedFile.content.rawContent.replace(IMPORT_SOLIDITY_REGEX, "").trim()
}
subtask("flat:get-flattened-sources", "Returns all contracts and their dependencies flattened")
.addOptionalParam("files", undefined, undefined, types.any)
.addOptionalParam("output", undefined, undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
const dependencyGraph = await run("flat:get-dependency-graph", { files })
let flattened = ""
if (dependencyGraph.getResolvedFiles().length === 0) {
return flattened
}
const sortedFiles = getSortedFiles(dependencyGraph)
let isFirst = true
for (const file of sortedFiles) {
if (!isFirst) {
flattened += "\n"
}
flattened += `// File ${file.getVersionedName()}\n`
flattened += `${getFileWithoutImports(file)}\n`
isFirst = false
}
// Remove every line started with "// SPDX-License-Identifier:"
flattened = flattened.replace(/SPDX-License-Identifier:/gm, "License-Identifier:")
flattened = `// SPDX-License-Identifier: MIXED\n\n${flattened}`
// Remove every line started with "pragma experimental ABIEncoderV2;" except the first one
flattened = flattened.replace(
/pragma experimental ABIEncoderV2;\n/gm,
(
(i) => (m) =>
!i++ ? m : ""
)(0)
)
flattened = flattened.trim()
if (output) {
console.log("Writing to", output)
fs.writeFileSync(output, flattened)
return ""
}
return flattened
})
subtask("flat:get-dependency-graph")
.addOptionalParam("files", undefined, undefined, types.any)
.setAction(async ({ files }, { run }) => {
const sourcePaths = files === undefined ? await run("compile:solidity:get-source-paths") : files.map((f) => fs.realpathSync(f))
const sourceNames = await run("compile:solidity:get-source-names", {
sourcePaths,
})
const dependencyGraph = await run("compile:solidity:get-dependency-graph", { sourceNames })
return dependencyGraph
})
task("flat", "Flattens and prints contracts and their dependencies")
.addOptionalVariadicPositionalParam("files", "The file or contract name to flatten", undefined, types.string)
.addOptionalParam("output", "Specify the output file", undefined, types.string)
.setAction(async ({ files, output }, { run }) => {
if (!output) {
output = "./flat/" + files[0] + ".sol"
}
files = files.map((file) => {
return file.endsWith(".sol")
? file
: "./contracts/" + file + ".sol"
})
await run("flat:get-flattened-sources", {
files,
output,
})
})