-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathsolc.ts
105 lines (88 loc) · 3.65 KB
/
solc.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
import { createHash } from 'crypto';
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
import * as path from 'path';
import type { ABI, SolcInput, SolcOutput } from 'solc';
import wrapper from 'solc/wrapper';
import { maskTitle } from './snapshot';
import type { Runnable, Suite } from 'mocha';
export const VERSIONS = ['0.5.5', '0.5.17', '0.6.12', '0.7.6', '0.8.16', '0.8.21'] as const;
export type Version = (typeof VERSIONS)[number];
const versionsLoaded = new Set<Version>();
/**
*
* https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description
*
* @param contractName
* @param content
* @param version
* @returns
*/
export function compile(
content: string,
version: Version,
ctx: Mocha.Context | null,
options?: SolcInput['settings'] & { ignoreWarnings?: boolean }
): { bytecode: string; abi: ABI; metadata: string, evm: SolcOutput['contracts'][string][string]['evm'] } {
const input = JSON.stringify({
language: 'Solidity',
sources: {
'source.sol': {
content: `// SPDX-License-Identifier: UNLICENSED\npragma solidity ${version};\n${content}`,
},
},
settings: {
optimizer: options?.optimizer,
metadata: options?.metadata,
outputSelection: {
'*': {
'*': ['abi', 'metadata', 'evm.deployedBytecode', 'evm.methodIdentifiers'],
},
},
},
} satisfies SolcInput);
let writeCacheFn: (output: ReturnType<typeof compile>) => void;
if (ctx !== null) {
const title = (test: Runnable | Suite | undefined): string =>
test ? title(test.parent) + '.' + test.title.replace(/^should /, '') : '';
const updateTitle = (text: string) => {
if (ctx.test) ctx.test.title += text;
};
const fileName = maskTitle(title(ctx.test))
.replace(`solc-v${version}.`, '')
.replace(/\."before-all"-hook-for-"[\w-#]+"/, '');
const basePath = `.artifacts/v${version}`;
if (!existsSync(basePath)) {
mkdirSync(basePath, { recursive: true });
}
const hash = createHash('md5').update(input).digest('hex').substring(0, 6);
const path = `${basePath}/${fileName}-${hash}`;
updateTitle(` #${hash}`);
try {
return JSON.parse(readFileSync(`${path}.json`, 'utf8')) as ReturnType<typeof compile>;
} catch {
updateTitle(' 🛠️');
if (!versionsLoaded.has(version)) {
ctx.timeout(ctx.timeout() + 5000);
updateTitle(`--loads \`solc-${version}\``);
}
writeCacheFn = output => writeFileSync(`${path}.json`, JSON.stringify(output, null, 2));
}
} else {
writeCacheFn = _output => { };
}
versionsLoaded.add(version);
// eslint-disable-next-line @typescript-eslint/no-var-requires
const solc = wrapper(require(path.resolve('.solc', `soljson-v${version}.js`)));
const { errors, contracts } = JSON.parse(solc.compile(input)) as SolcOutput;
if (errors !== undefined && (!options?.ignoreWarnings || errors.some(err => err.severity === 'error'))) {
throw new Error(errors.map(err => err.formattedMessage).join('\n'));
}
const source = contracts['source.sol'];
const contract = source['Test'] ?? Object.values(source)[0];
const bytecode = contract.evm.deployedBytecode.object;
const abi = contract.abi;
const metadata = contract.metadata;
const evm = contract.evm;
writeCacheFn({ bytecode, abi, metadata, evm });
return { bytecode, abi, metadata, evm };
}