Skip to content

Commit

Permalink
chore: replace solc dependency with a thin solidity_compile wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
galargh committed Dec 23, 2024
1 parent ed94118 commit 9904353
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 39 deletions.
52 changes: 18 additions & 34 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion v-next/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
"micro-eth-signer": "^0.13.0",
"p-map": "^7.0.2",
"semver": "^7.6.3",
"solc": "^0.8.27",
"tsx": "^4.11.0",
"ws": "^8.18.0",
"zod": "^3.23.8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ async function readStream(
}

async function getSolcJs(solcJsPath: string) {
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions --
We cast to string because it doesn't have types, and otherwise TS complains */
const { default: solcWrapper } = await import("solc/wrapper.js" as string);
const { default: solcWrapper } = await import("./solcjs-wrapper.js");
const { default: solc } = await import(solcJsPath);

return solcWrapper(solc);
Expand All @@ -28,7 +26,8 @@ async function main() {
const solcjsPath = process.argv[2];
const solc = await getSolcJs(solcjsPath);

const output = solc.compile(input);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- the input read from the stdin should be a string
const output = solc.compile(input as string);

console.log(output);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// This wrapper was created by extracting the parts of the solc-js package
// (https://github.com/ethereum/solc-js) that we need to perform compilation.

import * as semver from "semver";

interface Solc {
cwrap<T>(ident: string, returnType: string | null, argTypes: string[]): T;

// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_reset?: Reset | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_version?: Version | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_version?: Version | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_compileStandard?: Compile | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_compile?: Compile | null;
}

type Reset = () => string;
type Version = () => string;
type Compile = (
input: string,
callbackPtr: number | null,
callbackContextPtr?: null,
) => string;

export interface SolcWrapper {
compile: CompileWrapper;
}

export type CompileWrapper = (input: string) => string;

export default function wrapper(solc: Solc): SolcWrapper {
const version = bindVersion(solc);
const isVersion6OrNewer = semver.gte(versionToSemver(version()), "0.6.0");
const reset = bindReset(solc);
const compile = bindCompile(solc, isVersion6OrNewer);

if (compile === undefined) {
// eslint-disable-next-line no-restricted-syntax -- should we use a HardhatError here?
throw new Error(
'Could not find the "compile" function in the solc library',
);
}

return {
compile: compileWrapper(isVersion6OrNewer, compile, reset),
};
}

function compileWrapper(
isVersion6OrNewer: boolean,
compile: Compile,
reset?: Reset,
): CompileWrapper {
return (input: string): string => {
const output = isVersion6OrNewer
? compile(input, null, null)
: compile(input, null);

if (reset !== undefined) {
// Explicitly free memory.
//
// NOTE: cwrap() of "compile" will copy the returned pointer into a
// Javascript string and it is not possible to call free() on it.
// reset() however will clear up all allocations.
reset();
}

return output;
};
}

function bindVersion(solc: Solc): Version {
if (solc._solidity_version === null || solc._solidity_version === undefined) {
return solc.cwrap("version", "string", []);
}

return solc.cwrap("solidity_version", "string", []);
}

function bindReset(solc: Solc): Reset | undefined {
if (solc._solidity_reset === null || solc._solidity_reset === undefined) {
return undefined;
}

return solc.cwrap("solidity_reset", null, []);
}

function bindCompile(
solc: Solc,
isVersion6OrNewer: boolean,
): CompileWrapper | undefined {
if (isVersion6OrNewer) {
if (
solc._solidity_compile !== null &&
solc._solidity_compile !== undefined
) {
return solc.cwrap("solidity_compile", "string", [
"string",
"number",
"number",
]);
}
} else {
if (
solc._solidity_compile !== null &&
solc._solidity_compile !== undefined
) {
return solc.cwrap("solidity_compile", "string", ["string", "number"]);
}
if (solc._compileStandard !== null && solc._compileStandard !== undefined) {
return solc.cwrap("compileStandard", "string", ["string", "number"]);
}
}

return undefined;
}

function versionToSemver(version: string): string {
// FIXME: parse more detail, but this is a good start
const parsed = version.match(
/^([0-9]+\.[0-9]+\.[0-9]+)-([0-9a-f]{8})[/*].*$/,
);
if (parsed !== null) {
return parsed[1] + "+commit." + parsed[2];
}
if (version.indexOf("0.1.3-0") !== -1) {
return "0.1.3";
}
if (version.indexOf("0.3.5-0") !== -1) {
return "0.3.5";
}
// assume it is already semver compatible
return version;
}

0 comments on commit 9904353

Please sign in to comment.