This release adds support for Solidity 0.8.x, as well as multiple new features for customizing the behavior of the Hardhat Network.
New error messages
Some error messages returned by Hardhat have changed. If you have tests that check the exact message of a Hardhat error, you may need to update them.
For example, before this release a require(false, "this is the reason")
statement would've generated this error message:
Error: VM Exception while processing transaction: revert this is the reason
In this new version, the error message will be:
Error: VM Exception while processing transaction: reverted with reason string 'this is the reason'
You'll also get different error messages for panic errors (for example, if you are using solidity 0.8.0 and divide by zero) and for custom errors. See below to learn more about these new features.
New supported solidity features
Custom errors
Hardhat now recognizes custom errors. For example, given this contract:
contract Foo {
error MyCustomError(uint code, string message);
function test() public {
revert MyCustomError(42, "failed for some reason");
}
}
If you deploy Foo
and call its test
method, you'll now get a stack trace like this:
Error: VM Exception while processing transaction: reverted with custom error 'MyCustomError(42, "failed for some reason")'
at Foo.test (contracts/Foo.sol:5)
Keep in mind that Hardhat can only recognize custom errors that are defined within your project. If you use mainnet forking and call some external contract that reverts with a custom error, Hardhat won't be able to parse it, and it will show an unrecognized error instead.
Panic codes
Solidity 0.8.0 introduced the concept of panic codes. In previous solidity versions, internal errors like dividing by zero were signaled by an invalid opcode being executed. The problem with this was that there wasn't a way to detect what kind of internal error was thrown and, worse, an error like this would consume all the remaining gas available in the transaction. Now, instead of using an invalid opcode, these errors use the revert
opcode (the same used by the require
function) and include a code that indicates what was the problem that caused the error.
For example, if you deploy this contract:
contract Foo {
function test(uint x) public {
uint y = 1 / x;
}
}
and then you call the test
function with value 0, Hardhat will now show a stack trace like this:
Error: VM Exception while processing transaction: reverted with panic code 0x12 (Division or modulo division by zero)
at Foo.test (contracts/Foo.sol:3)
Top level functions
Hardhat now understands and generates correct stack traces when a top-level function is part of the call stack.
Customizing Hardhat Network's behavior
Arbitrary account modifications
Hardhat v2.4.0 includes several new RPC methods that let you modify anything about an address:
hardhat_setNonce
This method lets you change the nonce of an account. For example:
await network.provider.send("hardhat_setNonce", ["0x0d2026b3EE6eC71FC6746ADb6311F6d3Ba1C000B", "0x21"])
This will result in account 0x0d20...000B
having a nonce of 33.
You can only use this method to increase the nonce of an account; you can't set a lower value than the account's current nonce.
hardhat_setBalance
This method lets you change the balance of an account. For example:
await network.provider.send("hardhat_setBalance", ["0x0d2026b3EE6eC71FC6746ADb6311F6d3Ba1C000B", "0x1000"])
This will result in account 0x0d20...000B
having a balance of 4096 wei.
hardhat_setCode
This method lets you set the bytecode of an address. For example:
await network.provider.send("hardhat_setCode", ["0x0d2026b3EE6eC71FC6746ADb6311F6d3Ba1C000B", "0xa1a2a3..."])
This will result in account 0x0d20...000B
becoming a smart contract with bytecode a1a2a3...
. If that address was already a smart contract, then its code will be replaced by the specified one.
hardhat_setStorageAt
This method lets you modify any position in the storage of a smart contract. For example:
await network.provider.send("hardhat_setStorageAt", [
"0x0d2026b3EE6eC71FC6746ADb6311F6d3Ba1C000B",
"0x0",
"0x0000000000000000000000000000000000000000000000000000000000000001"
])
This will set the first storage position inside that contract to 1
.
The mapping between a smart contract's variables and its storage position is not straightforward except in some very simple cases. For example, if you deploy this contract:
contract Foo {
uint public x;
}
And you set the first storage position to 1 (as shown in the previous snippet), then calling foo.x()
will return 1.
Minimum gas price
You can now configure Hardhat Network so that it only mines transactions that have a gas price equal to or above a given threshold. If automining is enabled, transactions with a lower gas price will be rejected. If automining is disabled, this transactions will be kept in the mempool but they won't be mined.
To configure the minimum gas price, set the minGasPrice
property in the Hardhat Network configuration:
module.exports = {
networks: {
hardhat: {
minGasPrice: 1_000_000_000
}
}
}
You can also change this value in runtime using the new hardhat_setMinGasPrice
RPC method:
await network.provider.send("hardhat_setMinGasPrice", ["0x77359400"])
Transaction replacement
Hardhat now supports transaction replacement when automining is disabled. This means that if you send a transaction from the same address and with the same nonce as another transaction already present in the mempool, but with a higher gas price, this transaction will be replaced. The new gas price needs to be at least 10% higher than the gas price of the current transaction.
Dropping transactions
There is a new hardhat_dropTransaction
RPC method that can be used to remove transactions from the mempool:
const txHash = "0xabc..."
const result = await network.provider.send("hardhat_dropTransaction", [txHash])
There are three possible scenarios when this method is used:
txHash
corresponds to a transaction in the mempool. In this case, the transaction will be removed andresult
will betrue
.txHash
doesn't correspond to a transaction. In this case nothing will happen andresult
will befalse
.txHash
corresponds to a mined transaction. In this case anInvalidArgumentsError
will be thrown.
Other changes
-
Hardhat now supports the
HTTP_PROXY
andHTTPS_PROXY
environment variables. This is only used to download the Solidity compilers, not for JSON-RPC networks or mainnet forking. Thanks @cdesch for working on this! (PR #1291, issue #1280) -
When the balance of an account was set to a hexadecimal value, Hardhat would throw a very opaque error. Now a better error message is shown. Thanks to @Bidon15 for this contribution! (PR #1458, issue #1427)
-
Forking from xDai will cache data to disk if you ping a block older with 38 confirmations or more. Thanks to @jacobrosenthal for this! (PR #1557)