diff --git a/.changeset/sharp-rabbits-tell.md b/.changeset/sharp-rabbits-tell.md new file mode 100644 index 0000000000..8e4d54965b --- /dev/null +++ b/.changeset/sharp-rabbits-tell.md @@ -0,0 +1,5 @@ +--- +"hardhat": patch +--- + +Set the default evmVersion to paris for solc versions that are greater than or equal to 0.8.20. diff --git a/docs/src/content/hardhat-runner/docs/config/index.md b/docs/src/content/hardhat-runner/docs/config/index.md index 7c75b45341..b70fecaac6 100644 --- a/docs/src/content/hardhat-runner/docs/config/index.md +++ b/docs/src/content/hardhat-runner/docs/config/index.md @@ -208,3 +208,20 @@ You can configure how your tests are run using the `mocha` entry, which accepts ## Quickly integrating other tools from Hardhat's config Hardhat's config file will always run before any task, so you can use it to integrate with other tools, like importing `@babel/register`. + +## Default EVM Version + +The default EVM version is determined by solc's choice for a given compiler version. To specify a different EVM version, modify your `hardhat.config.js`: + +```js +module.exports = { + solidity: { + version: "0.8.21", + settings: { + evmVersion: "shanghai", + }, + }, +}; +``` + +Since version `0.8.20`, solc's EVM default is `shanghai`, which can lead to issues in chains that don't support the `PUSH0` opcode. To address this, starting from `0.8.20` Hardhat defaults to `paris`. This value can be overridden by using the above configuration. diff --git a/packages/hardhat-core/src/internal/core/config/config-resolution.ts b/packages/hardhat-core/src/internal/core/config/config-resolution.ts index eb5991aefe..b4f0e04fb8 100644 --- a/packages/hardhat-core/src/internal/core/config/config-resolution.ts +++ b/packages/hardhat-core/src/internal/core/config/config-resolution.ts @@ -1,5 +1,6 @@ import cloneDeep from "lodash/cloneDeep"; import path from "path"; +import semver from "semver"; import { HardhatConfig, @@ -385,6 +386,10 @@ function resolveCompiler(compiler: SolcUserConfig): SolcConfig { settings: compiler.settings ?? {}, }; + if (semver.gte(resolved.version, "0.8.20")) { + resolved.settings.evmVersion = compiler.settings?.evmVersion ?? "paris"; + } + resolved.settings.optimizer = { enabled: false, runs: 200, diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/.gitignore b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/.gitignore new file mode 100644 index 0000000000..e7f801166c --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/.gitignore @@ -0,0 +1,2 @@ +artifacts/ +cache/ diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/contracts/Lock.sol b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/contracts/Lock.sol new file mode 100644 index 0000000000..475d7cd83d --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/contracts/Lock.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; + +contract Lock { + uint public unlockTime; + address payable public owner; + + event Withdrawal(uint amount, uint when); + + constructor(uint _unlockTime) payable { + require( + block.timestamp < _unlockTime, + "Unlock time should be in the future" + ); + + unlockTime = _unlockTime; + owner = payable(msg.sender); + } + + function withdraw() public { + // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal + // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp); + + require(block.timestamp >= unlockTime, "You can't withdraw yet"); + require(msg.sender == owner, "You aren't the owner"); + + emit Withdrawal(address(this).balance, block.timestamp); + + owner.transfer(address(this).balance); + } +} diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/hardhat.config.js b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/hardhat.config.js new file mode 100644 index 0000000000..7bc6ff50f4 --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20-override-evm-version/hardhat.config.js @@ -0,0 +1,5 @@ +module.exports = { + solidity: { + compilers: [{ version: "0.8.20", settings: { evmVersion: "shanghai" } }], + }, +}; diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20/.gitignore b/packages/hardhat-core/test/fixture-projects/project-0.8.20/.gitignore new file mode 100644 index 0000000000..e7f801166c --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20/.gitignore @@ -0,0 +1,2 @@ +artifacts/ +cache/ diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20/contracts/Lock.sol b/packages/hardhat-core/test/fixture-projects/project-0.8.20/contracts/Lock.sol new file mode 100644 index 0000000000..475d7cd83d --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20/contracts/Lock.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.20; + +// Uncomment this line to use console.log +// import "hardhat/console.sol"; + +contract Lock { + uint public unlockTime; + address payable public owner; + + event Withdrawal(uint amount, uint when); + + constructor(uint _unlockTime) payable { + require( + block.timestamp < _unlockTime, + "Unlock time should be in the future" + ); + + unlockTime = _unlockTime; + owner = payable(msg.sender); + } + + function withdraw() public { + // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal + // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp); + + require(block.timestamp >= unlockTime, "You can't withdraw yet"); + require(msg.sender == owner, "You aren't the owner"); + + emit Withdrawal(address(this).balance, block.timestamp); + + owner.transfer(address(this).balance); + } +} diff --git a/packages/hardhat-core/test/fixture-projects/project-0.8.20/hardhat.config.js b/packages/hardhat-core/test/fixture-projects/project-0.8.20/hardhat.config.js new file mode 100644 index 0000000000..f6140ca394 --- /dev/null +++ b/packages/hardhat-core/test/fixture-projects/project-0.8.20/hardhat.config.js @@ -0,0 +1,3 @@ +module.exports = { + solidity: "0.8.20", +}; diff --git a/packages/hardhat-core/test/internal/core/config/config-resolution.ts b/packages/hardhat-core/test/internal/core/config/config-resolution.ts index f7511e43e0..49e6192ebd 100644 --- a/packages/hardhat-core/test/internal/core/config/config-resolution.ts +++ b/packages/hardhat-core/test/internal/core/config/config-resolution.ts @@ -24,6 +24,18 @@ import { HttpNetworkUserConfig, } from "../../../../src/types"; import { HardforkName } from "../../../../src/internal/util/hardforks"; +import { useFixtureProject } from "../../../helpers/project"; +import { + getAllFilesMatchingSync, + getRealPathSync, +} from "../../../../src/internal/util/fs-utils"; +import { useEnvironment } from "../../../helpers/environment"; + +function getBuildInfos() { + return getAllFilesMatchingSync(getRealPathSync("artifacts/build-info"), (f) => + f.endsWith(".json") + ); +} describe("Config resolution", () => { describe("Default config merging", () => { @@ -857,4 +869,119 @@ describe("Config resolution", () => { }); }); }); + + describe("evmVersion default", function () { + it("Should default to paris if solc is gte 0.8.20", () => { + let config = resolveConfig(__filename, { + solidity: "0.8.20", + }); + assert.equal(config.solidity.compilers[0]?.settings?.evmVersion, "paris"); + + config = resolveConfig(__filename, { + solidity: "0.8.21", + }); + assert.equal(config.solidity.compilers[0]?.settings?.evmVersion, "paris"); + + config = resolveConfig(__filename, { + solidity: { + compilers: [{ version: "0.8.20" }], + overrides: { + "contracts/ERC20.sol": { + version: "0.8.21", + }, + }, + }, + }); + assert.equal(config.solidity.compilers[0]?.settings?.evmVersion, "paris"); + assert.equal( + config.solidity.overrides["contracts/ERC20.sol"]?.settings?.evmVersion, + "paris" + ); + }); + + it("Should use the solc default if solc is lt 0.8.20", () => { + let config = resolveConfig(__filename, { + solidity: "0.8.19", + }); + assert.isUndefined(config.solidity.compilers[0]?.settings?.evmVersion); + + config = resolveConfig(__filename, { + solidity: { + compilers: [{ version: "0.5.7" }], + overrides: { + "contracts/ERC20.sol": { + version: "0.7.6", + }, + }, + }, + }); + assert.isUndefined(config.solidity.compilers[0]?.settings?.evmVersion); + assert.isUndefined( + config.solidity.overrides["contracts/ERC20.sol"]?.settings?.evmVersion + ); + }); + + it("Should let the user override the evmVersion", () => { + const config = resolveConfig(__filename, { + solidity: { + compilers: [ + { version: "0.8.20", settings: { evmVersion: "istanbul" } }, + ], + overrides: { + "contracts/ERC20.sol": { + version: "0.8.21", + settings: { evmVersion: "shanghai" }, + }, + }, + }, + }); + + assert.equal( + config.solidity.compilers[0]?.settings?.evmVersion, + "istanbul" + ); + assert.equal( + config.solidity.overrides["contracts/ERC20.sol"]?.settings?.evmVersion, + "shanghai" + ); + }); + + describe("With a solc 0.8.20 project and default config", () => { + useFixtureProject("project-0.8.20"); + useEnvironment(); + + it("Should not emit PUSH0 opcodes when compiling a contract with solc gte 0.8.20 and default config", async function () { + await this.env.run("compile"); + const source = "contracts/Lock.sol"; + const contract = "Lock"; + + const [buildInfo] = getBuildInfos(); + const { output } = require(buildInfo); + + assert.notInclude( + output.contracts[source][contract].evm.bytecode.opcodes, + "PUSH0" + ); + }); + }); + + describe("With a solc 0.8.20 project and overriden config", () => { + useFixtureProject("project-0.8.20-override-evm-version"); + useEnvironment(); + + it("Should emit PUSH0 opcodes when compiling a contract with solc gte 0.8.20 and overriden config", async function () { + await this.env.run("compile"); + const source = "contracts/Lock.sol"; + const contract = "Lock"; + + const [buildInfo] = getBuildInfos(); + const { output } = require(buildInfo); + + assert.include( + output.contracts[source][contract].evm.bytecode.opcodes, + "PUSH0" + ); + }); + }); + }); });