-
Notifications
You must be signed in to change notification settings - Fork 241
/
hardhat.ts
147 lines (130 loc) · 5 KB
/
hardhat.ts
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { findFiles } from "../../common/file-helper";
import { execute } from "../helpers/exec";
import { logger } from "../helpers/logger";
import { CompileOptions } from "../interfaces/Builder";
import { ContractPayload } from "../interfaces/ContractPayload";
import { BaseBuilder } from "./builder-base";
import { existsSync, readFileSync } from "fs";
import { HardhatConfig } from "hardhat/types";
import { join, resolve } from "path";
export class HardhatBuilder extends BaseBuilder {
public async compile(options: CompileOptions): Promise<{
contracts: ContractPayload[];
}> {
await execute("npx hardhat clean", options.projectPath);
await execute("npx hardhat compile", options.projectPath);
//we get our very own extractor script from the dir that we're in during execution
// this is `./dist/cli` (for all purposes of the CLI)
// then we look up the hardhat config extractor file path from there
const configExtractorScriptPath = resolve(
__dirname,
"../helpers/hardhat-config-extractor.js",
);
//the hardhat extractor **logs out** the runtime config of hardhat, we take that stdout and parse it
const stringifiedConfig = (
await execute(
`npx hardhat run "${configExtractorScriptPath}" --no-compile`,
options.projectPath,
)
).stdout;
//voila the hardhat config
const actualHardhatConfig = JSON.parse(
stringifiedConfig.split("__tw__")[1],
) as HardhatConfig;
logger.debug("successfully extracted hardhat config", actualHardhatConfig);
const solcConfigs = actualHardhatConfig.solidity.compilers;
if (solcConfigs) {
for (const solcConfig of solcConfigs) {
const byteCodeHash = solcConfig.settings?.metadata?.bytecodeHash;
if (byteCodeHash && byteCodeHash !== "ipfs") {
throw new Error(
`Deploying requires "bytecodeHash: 'ipfs'" in your hardhat.config.js file, but it's currently set as "bytecodeHash: '${byteCodeHash}'". Please change it to 'ipfs' and try again.`,
);
}
}
}
const artifactsPath = actualHardhatConfig.paths.artifacts;
const sourcesDir = actualHardhatConfig.paths.sources.replace(
options.projectPath,
"",
);
const contractsPath = join(artifactsPath, sourcesDir);
const contracts: ContractPayload[] = [];
const files: string[] = [];
findFiles(contractsPath, /^.*(?<!dbg)\.json$/, files);
const buildOutputPath = join(artifactsPath, "build-info");
const buildFiles: string[] = [];
findFiles(buildOutputPath, /^.*(?<!dbg)\.json$/, buildFiles);
for (const buildFile of buildFiles) {
const buildJsonFile = readFileSync(buildFile, "utf-8");
const buildJson = JSON.parse(buildJsonFile);
const contractBuildOutputs = buildJson.output.contracts;
for (const [contractPath, contractInfos] of Object.entries(
contractBuildOutputs,
)) {
if (contractPath.includes("@")) {
// skip library contracts
logger.debug("Skipping", contractPath, "(not a source target)");
continue;
}
for (const [contractName, contractInfo] of Object.entries(
contractInfos as any,
)) {
const info = contractInfo as any;
if (
!info.evm ||
!info.evm.bytecode ||
!info.evm.bytecode.object ||
!info.metadata ||
!info.abi
) {
logger.debug("Skipping", contractPath, "(no bytecode or metadata)");
continue;
}
const bytecode = info.evm.bytecode.object;
const deployedBytecode = info.evm.deployedBytecode.object;
const metadata = info.metadata;
const abi = info.abi;
const meta = JSON.parse(metadata);
const sources = Object.keys(meta.sources)
.map((path) => {
const directPath = join(options.projectPath, path);
if (existsSync(directPath)) {
return directPath;
}
const sourcePath = join(options.projectPath, sourcesDir, path);
if (existsSync(sourcePath)) {
return sourcePath;
}
const nodeModulesPath = join(
options.projectPath,
"node_modules",
path,
);
if (existsSync(nodeModulesPath)) {
return nodeModulesPath;
}
return undefined;
})
.filter((path) => path !== undefined) as string[];
const fileNames = Object.keys(
meta?.settings?.compilationTarget || {},
);
const fileName = fileNames.length > 0 ? fileNames[0] : "";
if (this.shouldProcessContract(abi, deployedBytecode, contractName)) {
contracts.push({
metadata,
bytecode,
name: contractName,
fileName,
sources,
});
}
}
}
}
return {
contracts,
};
}
}