Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
Draft basics of Hardhat compatibility
Browse files Browse the repository at this point in the history
The idea being to expose, roughly:
- detectHardhat() - to performantly check if it's a Hardhat project
- prepareConfig() - to produce a TruffleConfig with networks
- prepareCompilation() - to get the good stuff
  • Loading branch information
gnidan committed Aug 10, 2022
1 parent 2f9715c commit bcedc89
Show file tree
Hide file tree
Showing 6 changed files with 731 additions and 32 deletions.
2 changes: 2 additions & 0 deletions packages/from-hardhat/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist
node_modules
52 changes: 52 additions & 0 deletions packages/from-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@truffle/from-hardhat",
"description": "Import Hardhat project information into Truffle-native formats",
"license": "MIT",
"author": "g. nicholas d'andrea <gnidan@trufflesuite.com>",
"homepage": "https://github.com/trufflesuite/truffle/tree/master/packages/from-hardhat#readme",
"repository": {
"type": "git",
"url": "https://github.com/trufflesuite/truffle.git",
"directory": "packages/from-hardhat"
},
"bugs": {
"url": "https://github.com/trufflesuite/truffle/issues"
},
"version": "0.1.0-0",
"main": "dist/src/index.js",
"scripts": {
"build": "ttsc",
"prepare": "yarn build",
"test": "exit 0"
},
"dependencies": {
"@truffle/compile-common": "^0.7.32",
"@truffle/compile-solidity": "^6.0.38",
"@truffle/config": "^1.3.34",
"debug": "^4.3.1",
"find-up": "^2.1.0"
},
"devDependencies": {
"@types/find-up": "^2.1.0",
"@types/node": "^18.6.5",
"hardhat": "^2.10.1",
"ttypescript": "1.5.13",
"typescript": "^4.3.5",
"typescript-transform-paths": "3.3.1"
},
"keywords": [
"compile",
"ethereum",
"solidity",
"truffle"
],
"publishConfig": {
"access": "public"
},
"babel": {
"presets": [
"env"
]
},
"gitHead": "6b84be7849142588ef2e3224d8a9d7c2ceeb6e4a"
}
114 changes: 114 additions & 0 deletions packages/from-hardhat/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import type * as Hardhat from "hardhat/types";
import type * as Common from "@truffle/compile-common";
import findUp from "find-up";
import Config from "@truffle/config";
import { spawn } from "child_process";
import { promises as fs } from "fs";

import { buildInfoCompilation } from "./shims";

const validHardhatConfigFilenames = ["hardhat.config.js", "hardhat.config.ts"];

// import type * as Hardhat from "hardhat";

export async function detectHardhat(): Promise<boolean> {
// search recursively up for a hardhat config
const hardhatConfigPath = await findUp(validHardhatConfigFilenames);

return !!hardhatConfigPath;
}

export async function prepareConfig(): Promise<Config> {
const config = Config.default();

const networks = (await askHardhatConsole(
`hre.config.networks`
)) as Hardhat.NetworksUserConfig;

for (const [networkName, networkConfig] of Object.entries(networks)) {
if (networkName === "hardhat") {
continue;
}

if (networkConfig && "url" in networkConfig) {
const { url } = networkConfig;
config.networks[networkName] = {
url,
network_id: "*"
};
}
}

return config;
}

export async function prepareCompilations(): Promise<Common.Compilation[]> {
const compilations = [];

const buildInfoPaths = (await askHardhatConsole(
`artifacts.getBuildInfoPaths()`
)) as string[];

for (const buildInfoPath of buildInfoPaths) {
const buildInfo: Hardhat.BuildInfo = JSON.parse(
(await fs.readFile(buildInfoPath)).toString()
);

const compilation = buildInfoCompilation(buildInfo);

compilations.push(compilation);
}

return compilations;
}

interface AskHardhatConsoleOptions {
// turn off json stringify/parse
raw?: boolean;
}

async function askHardhatConsole(
expression: string,
options: AskHardhatConsoleOptions = {}
): Promise<string | unknown> {
const { raw = false } = options;

return new Promise((accept, reject) => {
const hardhat = spawn(`npx`, ["hardhat", "console"], {
stdio: ["pipe", "pipe", "inherit"]
});

// we'll capture the stdout
let output = "";
hardhat.stdout.on("data", data => {
output = `${output}${data}`;
});

// setup close event before writing to stdin because we're sending eof
hardhat.on("close", code => {
if (code !== 0) {
return reject(new Error(`Hardhat exited with non-zero code ${code}`));
}

if (raw) {
return accept(output);
}

try {
return accept(JSON.parse(output));
} catch (error) {
return reject(error);
}
});

hardhat.stdin.write(`
Promise.resolve(${expression})
.then(${
raw
? `console.log`
: `(resolved) => console.log(JSON.stringify(resolved))`
})
`);
hardhat.stdin.end();
});
}
116 changes: 116 additions & 0 deletions packages/from-hardhat/src/shims.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import type * as Hardhat from "hardhat/types";

import type { Compilation, CompiledContract } from "@truffle/compile-common";
import * as CompileSolidity from "@truffle/compile-solidity";

const supportedFormats = new Set(["hh-sol-build-info-1"]);

export function buildInfoCompilation(
buildInfo: Hardhat.BuildInfo
): Compilation {
const { _format } = buildInfo;

if (!supportedFormats.has(_format)) {
throw new Error(`Unsupported build info format: ${_format}`);
}

const sourceIndexes = buildInfoSourceIndexes(buildInfo);

return {
sourceIndexes,
sources: buildInfoSources(buildInfo, sourceIndexes),
contracts: buildInfoContracts(buildInfo),
compiler: {
name: "solc",
version: buildInfo.solcLongVersion
}
};
}

function buildInfoSourceIndexes(
buildInfo: Hardhat.BuildInfo
): Compilation["sourceIndexes"] {
const sourceIndexes = [];
for (const { index, sourcePath } of Object.entries(
buildInfo.output.sources
).map(([sourcePath, source]) => ({ index: source.id, sourcePath }))) {
sourceIndexes[index] = sourcePath;
}

return sourceIndexes;
}

function buildInfoSources(
buildInfo: Hardhat.BuildInfo,
sourceIndexes: Compilation["sourceIndexes"]
): Compilation["sources"] {
return sourceIndexes.map(sourcePath => {
// to handle if sourceIndexes is a sparse array
if (!sourcePath) {
return;
}

const inputSource = buildInfo.input.sources[sourcePath];
const outputSource = buildInfo.output.sources[sourcePath];

return {
sourcePath,
contents: inputSource.content,
ast: outputSource.ast,
language: buildInfo.input.language
};
});
}

function buildInfoContracts(buildInfo: Hardhat.BuildInfo): CompiledContract[] {
const contracts = [];
for (const [sourcePath, sourceContracts] of Object.entries(
buildInfo.output.contracts
)) {
for (const [contractName, compilerOutputContract] of Object.entries(
sourceContracts
)) {
const contract: CompiledContract = {
contractName,
sourcePath,
source: buildInfo.input.sources[sourcePath].content,
sourceMap: compilerOutputContract.evm.bytecode.sourceMap,
deployedSourceMap:
compilerOutputContract.evm.deployedBytecode.sourceMap,
legacyAST: undefined,
ast: buildInfo.output.sources[sourcePath].ast,
abi: compilerOutputContract.abi,
metadata: (compilerOutputContract as any).metadata,
bytecode: CompileSolidity.Shims.zeroLinkReferences({
bytes: compilerOutputContract.evm.bytecode.object,
linkReferences: CompileSolidity.Shims.formatLinkReferences(
compilerOutputContract.evm.bytecode.linkReferences
)
}),
deployedBytecode: CompileSolidity.Shims.zeroLinkReferences({
bytes: compilerOutputContract.evm.deployedBytecode.object,
linkReferences: CompileSolidity.Shims.formatLinkReferences(
compilerOutputContract.evm.deployedBytecode.linkReferences
)
}),
compiler: {
name: "solc",
version: buildInfo.solcLongVersion
},
devdoc: undefined,
userdoc: undefined,
immutableReferences:
compilerOutputContract.evm.deployedBytecode.immutableReferences,
generatedSources: (compilerOutputContract.evm.bytecode as any)
.generatedSources,
deployedGeneratedSources: (
compilerOutputContract.evm.deployedBytecode as any
).generatedSources
};

contracts.push(contract);
}
}

return contracts;
}
39 changes: 39 additions & 0 deletions packages/from-hardhat/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"compilerOptions": {
"sourceMap": true,
"declaration": true,
"allowJs": false,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"skipLibCheck": true,
"target": "es2016",
"moduleResolution": "node",
"downlevelIteration": true,
"allowSyntheticDefaultImports": true,
"module": "commonjs",
"outDir": "./dist",
"strictBindCallApply": true,
"paths": {
"@truffle/from-hardhat": ["./src"],
"@truffle/from-hardhat/*": ["./src/*"],
"test/*": ["./test/*"]
},
"rootDir": ".",
"baseUrl": ".",
"typeRoots": ["../../node_modules/@types/node"],
"types": [
"mocha"
],
"plugins": [
{ "transform": "typescript-transform-paths" },
{ "transform": "typescript-transform-paths", "afterDeclarations": true }
]
},
"include": [
"src/**/*"
],
"exclude": [
"dist",
"node_modules"
]
}
Loading

0 comments on commit bcedc89

Please sign in to comment.