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

Commit

Permalink
Devise new package @truffle/from-hardhat
Browse files Browse the repository at this point in the history
... for compatibility translation
  • Loading branch information
gnidan committed Aug 13, 2022
1 parent a5cd1d7 commit 6e9b1ff
Show file tree
Hide file tree
Showing 13 changed files with 939 additions and 33 deletions.
3 changes: 3 additions & 0 deletions packages/from-hardhat/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["../../.eslintrc.package.json"]
}
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
8 changes: 8 additions & 0 deletions packages/from-hardhat/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# @truffle/from-hardhat

> :warning: **This package is experimental and FOR INTERNAL USE ONLY.**
This package translates Hardhat project information into Truffle's own formats.

For information on using this package (until we get it more ready for public
use), please see [`./src/api.ts`](src/api.ts).
47 changes: 47 additions & 0 deletions packages/from-hardhat/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"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",
"semver": "^5.7.1"
},
"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": [
"ethereum",
"solidity",
"truffle",
"hardhat"
],
"publishConfig": {
"access": "public"
}
}
142 changes: 142 additions & 0 deletions packages/from-hardhat/src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { promises as fs } from "fs";
import semver from "semver";

import type * as Hardhat from "hardhat/types";
import type * as Common from "@truffle/compile-common";
import type TruffleConfig from "@truffle/config";

import { supportedHardhatVersionRange } from "./constants";
import * as Compilation from "./compilation";
import * as Config from "./config";

import {
checkHardhat,
askHardhatConsole,
askHardhatVersion
} from "./ask-hardhat";
import { EnvironmentOptions } from "./options";

/**
* Checks for the existence of a Hardhat project configuration and asserts
* that the local installed version of Hardhat matches this package's
* supported version range.
*
* @param options to control process environment (e.g. working directory)
* @return Promise<void> when expectation holds
* @throws NotHardhatError when not in a Hardhat project directory
* @throws IncompatibleHardhatError if Hardhat has unsupported version
*/
export const expectHardhat = async (
options?: EnvironmentOptions
): Promise<void> => {
const isHardhat = await checkHardhat(options);

if (!isHardhat) {
throw new NotHardhatError();
}

const hardhatVersion = await askHardhatVersion(options);

if (!semver.satisfies(hardhatVersion, supportedHardhatVersionRange)) {
throw new IncompatibleHardhatError(hardhatVersion);
}
};

/**
* Thrown when no Hardhat project is found
*/
export class NotHardhatError extends Error {
constructor() {
super("Current working directory is not part of a Hardhat project");
}
}

/**
* Thrown when Hardhat was detected but with an incompatible version
*/
export class IncompatibleHardhatError extends Error {
constructor(detectedVersion: string) {
super(
`Expected Hardhat version compatible with ${supportedHardhatVersionRange}, got: ${detectedVersion}`
);
}
}

/**
* Constructs a @truffle/config object based on the Hardhat config.
*
* WARNING: except for fields documented here, the values present on the
* returned @truffle/config object MUST be regarded as unsafe to use.
*
* The returned `config` is defined to contain the following:
*
* - `config.networks` with configurations for all Hardhat-configured
* networks, provided:
* - The configured network is not the built-in `hardhat` network
* - The configured network defines a `url` property
*
* Note: this function ignores all properties other than `url`,
* including any information that can be used for computing
* cryptographic signatures. THIS FUNCTION DOES NOT READ PRIVATE KEYS.
*
* Suffice to say:
*
* THIS FUNCTION'S BEHAVIOR IS EXPERIMENTAL AND SHOULD ONLY BE USED IN
* SPECIFICALLY KNOWN-SUPPORTED USE CASES (like reading for configured
* network urls)
*
* @param options to control process environment (e.g. working directory)
* @return Promise<TruffleConfig> with the following fields:
*
* @dev This function shells out to `npx hardhat console` to ask the Hardhat
* runtime environment for a fully populated config object.
*/
export const prepareConfig = async (
options?: EnvironmentOptions
): Promise<TruffleConfig> => {
const hardhatConfig = (await askHardhatConsole(
`hre.config`,
options
)) as Hardhat.HardhatConfig;

return Config.fromHardhatConfig(hardhatConfig);
};

/**
* Constructs an array of @truffle/compile-common `Compilation` objects
* corresponding one-to-one with Hardhat's persisted results of each solc
* compilation.
*
* WARNING: this function only supports Hardhat projects written entirely
* in solc-compatible languages (Solidity, Yul). Behavior of this function
* for Hardhat projects using other languages is undefined.
*
* @param options to control process environment (e.g. working directory)
* @return Promise<Compilation[]> from @truffle/compile-common
*
* @dev This function shells out to `npx hardhat console` to ask the Hardhat
* runtime environment for the location of the project build info
* files
*/
export const prepareCompilations = async (
options?: EnvironmentOptions
): Promise<Common.Compilation[]> => {
const compilations = [];

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

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

const compilation = Compilation.fromBuildInfo(buildInfo);

compilations.push(compilation);
}

return compilations;
};
108 changes: 108 additions & 0 deletions packages/from-hardhat/src/ask-hardhat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { spawn } from "child_process";

import findUp from "find-up";

import { validHardhatConfigFilenames } from "./constants";
import { EnvironmentOptions, withDefaultEnvironmentOptions } from "./options";

/**
* Returns a Promise to a boolean that is true if and only if
* the detected or specified environment is part of a Hardhat project.
*
* (i.e., if the working directory or any of its parents has a Hardhat config)
*/
export const checkHardhat = async (
options?: EnvironmentOptions
): Promise<boolean> => {
const { workingDirectory } = withDefaultEnvironmentOptions(options);

// search recursively up for a hardhat config
const hardhatConfigPath = await findUp(validHardhatConfigFilenames, {
cwd: workingDirectory
});

return !!hardhatConfigPath;
};

/**
* Reads version information via `npx hardhat --version`
*/
export const askHardhatVersion = async (
options?: EnvironmentOptions
): Promise<string> =>
new Promise((accept, reject) => {
const { workingDirectory } = withDefaultEnvironmentOptions(options);

const hardhat = spawn(`npx`, ["hardhat", "--version"], {
stdio: "pipe",
cwd: workingDirectory
});

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}`));
}

return accept(output);
});
});

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

export const askHardhatConsole = async (
expression: string,
{
raw = false,
...options
}: AskHardhatConsoleOptions & EnvironmentOptions = {}
): Promise<string | unknown> =>
new Promise((accept, reject) => {
const { workingDirectory } = withDefaultEnvironmentOptions(options);

const hardhat = spawn(`npx`, ["hardhat", "console"], {
stdio: ["pipe", "pipe", "inherit"],
cwd: workingDirectory
});

// 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();
});
Loading

0 comments on commit 6e9b1ff

Please sign in to comment.