diff --git a/hardhat.config.ts b/hardhat.config.ts index 329a02af3..1fb5d9fdb 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,41 +1,13 @@ require("dotenv").config(); -const replace = require("replace-in-file"); -let changedFiles; - -import { HardhatUserConfig, internalTask } from "hardhat/config"; -import { TASK_COMPILE_SOLIDITY_COMPILE, TASK_TEST_SETUP_TEST_ENVIRONMENT } from "hardhat/builtin-tasks/task-names"; -import { execSync } from "child_process"; +import { HardhatUserConfig } from "hardhat/config"; import { privateKeys } from "./utils/wallets"; import "@nomiclabs/hardhat-waffle"; import "hardhat-typechain"; import "solidity-coverage"; import "hardhat-deploy"; - -internalTask(TASK_COMPILE_SOLIDITY_COMPILE).setAction(setupNativeSolc); -internalTask(TASK_TEST_SETUP_TEST_ENVIRONMENT).setAction(async function setupNativeSolc({ input }, { config }, runSuper) { - // Fix gas to be string instead of number in typechain files - const options = { - // Glob(s) - files: [ - "./typechain/**/*.ts", - ], - - // Replacement to make (string or regex) - from: /gas\: [0-9]+,/g, - to: (match: string) => match.replace(/gas: ([0-9]+),/g, 'gas: "$1",'), - }; - - try { - changedFiles = replace.sync(options); - console.log("Fixing gas cost type from number to string...") - ; - } - catch (error) { - console.error("Error occurred:", error); - } -}); +import "./tasks"; const config: HardhatUserConfig = { solidity: { @@ -83,7 +55,7 @@ const config: HardhatUserConfig = { }, mocha: { timeout: 100000, - } + }, }; function getHardhatPrivateKeys() { @@ -96,28 +68,4 @@ function getHardhatPrivateKeys() { }); } -// @ts-ignore -async function setupNativeSolc({ input }, { config }, runSuper) { - let solcVersionOutput = ""; - try { - solcVersionOutput = execSync(`solc --version`).toString(); - } catch (error) { - // Probably failed because solc wasn"t installed. We do nothing here. - } - - console.log("Output", solcVersionOutput); - - if (!solcVersionOutput.includes(config.solidity.version)) { - console.log(`Using solcjs`); - return runSuper(); - } - - console.log(`Using native solc`); - const output = execSync(`solc --standard-json`, { - input: JSON.stringify(input, undefined, 2), - }); - - return JSON.parse(output.toString(`utf8`)); -} - export default config; diff --git a/package.json b/package.json index ebbc89095..c0ad1fe31 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "chain": "npx hardhat node --no-deploy", "clean": "rm -f coverage.json; rm -rf .coverage_cache; rm -rf .coverage_contracts; rm -rf cache; rm -rf coverage; rm -rf typechain; rm -rf artifacts", "compile": "npx hardhat compile", - "coverage": "yarn clean && yarn build && npx hardhat coverage --temp artifacts", + "coverage": "yarn clean && yarn build && yarn cov:command", + "cov:command": "COVERAGE=true node --max-old-space-size=4096 ./node_modules/.bin/hardhat coverage", "etherscan:verify": "hardhat --network kovan etherscan-verify --solc-input --license 'None'", "fix-typechain": "node scripts/fix-typechain.js && yarn rename-extensions", "flatten": "npx waffle flatten", diff --git a/tasks/index.ts b/tasks/index.ts new file mode 100644 index 000000000..531b1c5a0 --- /dev/null +++ b/tasks/index.ts @@ -0,0 +1 @@ +export * from "./subtasks"; diff --git a/tasks/subtasks.ts b/tasks/subtasks.ts new file mode 100644 index 000000000..d096fcdcd --- /dev/null +++ b/tasks/subtasks.ts @@ -0,0 +1,43 @@ +import { + TASK_COMPILE_SOLIDITY_COMPILE, + TASK_TEST_SETUP_TEST_ENVIRONMENT, + TASK_COMPILE_SOLIDITY_GET_ARTIFACT_FROM_COMPILATION_OUTPUT, +} from "hardhat/builtin-tasks/task-names"; + +import { subtask, internalTask } from "hardhat/config"; +import { addGasToAbiMethods, setupNativeSolc } from "../utils/tasks"; + +// Injects network block limit (minus 1 million) in the abi so +// ethers uses it instead of running gas estimation. +subtask(TASK_COMPILE_SOLIDITY_GET_ARTIFACT_FROM_COMPILATION_OUTPUT) + .setAction(async (_, { network }, runSuper) => { + const artifact = await runSuper(); + artifact.abi = addGasToAbiMethods(network.config, artifact.abi); + return artifact; + } +); + +// Use native solc if available locally at config specified version +internalTask(TASK_COMPILE_SOLIDITY_COMPILE).setAction(setupNativeSolc); + +// Fix gas to be string instead of number in typechain files +internalTask(TASK_TEST_SETUP_TEST_ENVIRONMENT) + .setAction(async function setupNativeSolc({ input }, { config }, runSuper) { + const replace = require("replace-in-file"); + + const options = { + files: [ "./typechain/**/*.ts" ], + from: /gas\: [0-9]+,/g, + to: (match: string) => match.replace(/gas: ([0-9]+),/g, 'gas: "$1",'), + }; + + try { + replace.sync(options); + console.log("Fixing gas cost type from number to string..."); + } catch (error) { + console.error("Error occurred:", error); + } +}); + + +export {}; diff --git a/tsconfig.json b/tsconfig.json index 5b569533a..c7af42d89 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,13 @@ "@typechain/*": ["typechain/*"], } }, - "include": ["./test/**/*.ts", "./utils/**/*.ts", "./deploy/**/*.ts", "./typechain/**/*.ts"], + "include": [ + "./test/**/*.ts", + "./utils/**/*.ts", + "./deploy/**/*.ts", + "./typechain/**/*.ts", + "./tasks/**/*.ts" + ], "files": [ "hardhat.config.ts", ] diff --git a/utils/tasks/artifactUtils.ts b/utils/tasks/artifactUtils.ts new file mode 100644 index 000000000..1a3483398 --- /dev/null +++ b/utils/tasks/artifactUtils.ts @@ -0,0 +1,42 @@ +import type { ethers } from "ethers"; +import { NetworkConfig } from "hardhat/types"; + + +// Adds a `gas` field to the ABI function elements so that ethers doesn't +// automatically estimate gas limits on every call. Halves execution time. +// (Borrowed from hardhat-ethers/src/internal/helpers.ts) +export function addGasToAbiMethods( + networkConfig: NetworkConfig, + abi: any[] +): any[] { + const { BigNumber } = require("ethers") as typeof ethers; + + // Stay well under network limit b/c ethers adds a margin + // Also need special setting logic for coverage b/c it compiles + // before configuring the network with higher gas values. + let gas: number; + if (process.env.COVERAGE === "true") { + const CoverageAPI: any = require("solidity-coverage/api"); + gas = new CoverageAPI().gasLimit as number; + } else { + gas = networkConfig.gas as number; + } + + const gasLimit = BigNumber.from(gas).sub(1000000).toHexString(); + + const modifiedAbi: any[] = []; + + for (const abiElement of abi) { + if (abiElement.type !== "function") { + modifiedAbi.push(abiElement); + continue; + } + + modifiedAbi.push({ + ...abiElement, + gas: gasLimit, + }); + } + + return modifiedAbi; +} diff --git a/utils/tasks/index.ts b/utils/tasks/index.ts new file mode 100644 index 000000000..423e0230e --- /dev/null +++ b/utils/tasks/index.ts @@ -0,0 +1,2 @@ +export * from "./artifactUtils"; +export * from "./setupNativeSolc"; diff --git a/utils/tasks/setupNativeSolc.ts b/utils/tasks/setupNativeSolc.ts new file mode 100644 index 000000000..9226b5438 --- /dev/null +++ b/utils/tasks/setupNativeSolc.ts @@ -0,0 +1,30 @@ +import { execSync } from "child_process"; + +let isFirstRun = true; + +// @ts-ignore +export async function setupNativeSolc({ input }, { config }, runSuper) { + let solcVersionOutput = ""; + try { + solcVersionOutput = execSync(`solc --version`).toString(); + } catch (error) { + // Probably failed because solc wasn"t installed. We do nothing here. + } + + isFirstRun && console.log("Local native solc version: ", solcVersionOutput); + + if (!solcVersionOutput.includes(config.solidity.version)) { + isFirstRun && console.log(`Using solcjs`); + isFirstRun = false; + return runSuper(); + } + + isFirstRun && console.log(`Using native solc`); + isFirstRun = false; + + const output = execSync(`solc --standard-json`, { + input: JSON.stringify(input, undefined, 2), + }); + + return JSON.parse(output.toString(`utf8`)); +}