Skip to content

Commit

Permalink
Merge pull request #5939 from NomicFoundation/update-ethers-template
Browse files Browse the repository at this point in the history
Update the `mocha`+`ethers` template
  • Loading branch information
alcuadrado authored Nov 7, 2024
2 parents a50bf61 + 0d8c5b6 commit bdf4a59
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 60 deletions.
76 changes: 59 additions & 17 deletions v-next/hardhat/templates/mocha-ethers/README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,76 @@
# A TypeScript Hardhat project using Mocha and Ethers
# Hardhat 3 Alpha: `mocha` and `ethers` example project

> WARNING: This demonstration project is still in development. It is part of the Hardhat v3 upgrade. It is not for production use.
> **WARNING**: This example project uses Hardhat 3, which is still in development. Hardhat 3 is not yet intended for production use.
> NOTE: There are several plugins from the Hardhat toolbox that have not been ported to Hardhat v3 yet. In testing terms, the biggest ommision is `hardhat-chai-matchers`.
Welcome to the Hardhat 3 alpha version! This project showcases some of the changes and new features coming in Hardhat 3.

This project demonstrates basic Hardhat usecases within a TypeScript project. It comes with:
To learn more about the Hardhat 3 alpha, please visit [its Docs Hub](https://www.notion.so/nomicfoundation/Hardhat-3-alpha-Docs-Hub-131578cdeaf580e89e8dca57b0d036c3).

- a minimal Hardhat configuration file
- JS/TS integration tests
- Solidity Tests
- A script demonstrating how to deploy a contract to an in-memory Hardhat node simulating Base (an Optimism l2 chain)
## Project Overview

## Usage
This example project includes:

- A simple Hardhat configuration file
- TypeScript integration tests using `mocha` and ethers.js
- Foundry-compatible Solidity tests, including the usage of `forge-std`
- Examples demonstrating how to connect to different types of networks, including simulating an Optimism network
- A script that deploys a contract to Optimism Sepolia using Hardhat's new keystore capabilities

## Navigating the Project

To get the most out of this example project, we recommend exploring the files in the following order:

### Testing
1. Read the `hardhat.config.ts` file, which contains the project configuration and explains multiple changes.
2. Review the "Running Tests" section and explore the files in the `contracts/` and `test/` directories.
3. Read the "Sending a Transaction to Optimism Sepolia" section, follow the instructions, and examine the `scripts/send-op-tx.ts` file.

Each file includes inline explanations of its purpose and highlights the changes and new features introduced in Hardhat 3.

## Usage

For integration testing it uses the Mocha test runner and the Ethers.js library for interacting with Ethereum nodes.
### Running Tests

Try running the following commands to see Hardhat's testing in action:
To run all the tests in the project, execute the following command:

```shell
# Run the both the mocha integration test suite
# and the Solidity Test suite
npx hardhat3 test
```

### Multi-chain support
You can also selectively run the Solidity or `mocha` tests:

To deploy a contract to an in-memory Hardhat node simulating Base (an Optimism l2 chain), run:
```shell
npx hardhat3 test solidity
npx hardhat3 test mocha
```

### Sending a Transaction to Optimism Sepolia

This project includes an example script that sends a simple transaction to Optimism Sepolia. You can run the script using either the actual Optimism Sepolia network or a simulated version that behaves exactly like the real network.

To run the script with EDR in Optimism mode:

```shell
npx hardhat3 run scripts/deploy-counter-contract.ts
npx hardhat3 run scripts/send-op-tx.ts --network edrOpSepolia
```

To run the script with Optimism Sepolia, you need an account with funds to send the transaction. The provided Hardhat configuration includes a Configuration Variable called `OPTIMISM_SEPOLIA_PRIVATE_KEY`, which you can use to set the private key of the account you want to use.

You can set the `OPTIMISM_SEPOLIA_PRIVATE_KEY` variable using the `hardhat-keystore` plugin or by setting it as an environment variable.

> **WARNING**: The private key is currently stored unencrypted. Full encryption will be included before the beta release.
To set the `OPTIMISM_SEPOLIA_PRIVATE_KEY` config variable using `hardhat-keystore`:

```shell
npx hardhat3 keystore set OPTIMISM_SEPOLIA_PRIVATE_KEY
```

After setting the variable, you can run the script with the Optimism Sepolia network:

```shell
npx hardhat3 run scripts/send-op-tx.ts --network opSepolia
```

---

Feel free to explore the project and provide feedback on your experience with Hardhat 3 alpha!
3 changes: 3 additions & 0 deletions v-next/hardhat/templates/mocha-ethers/contracts/Counter.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// This is a simple smart contract used to demonstrate
// a Solidity test in `Counter.t.sol`.

contract Counter {
uint public x;

Expand Down
8 changes: 5 additions & 3 deletions v-next/hardhat/templates/mocha-ethers/contracts/Counter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@ pragma solidity ^0.8.24;

import "./Counter.sol";

// Solidity tests are compatible with foundry, so they
// use the same syntax and offer the same functionality.

contract CounterTest {
Counter counter;

function setUp() public {
counter = new Counter();
}

function testInitialValue() public view {
function test_InitialValue() public view {
require(counter.x() == 0, "Initial value should be 0");
}

function testFuzzInc(uint8 x) public {
function testFuzz_Inc(uint8 x) public {
for (uint8 i = 0; i < x; i++) {
counter.inc();
}

require(counter.x() == x, "Value after calling inc x times should be x");
}
}
4 changes: 4 additions & 0 deletions v-next/hardhat/templates/mocha-ethers/contracts/Lock.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.24;

// This is the same example of Hardhat 2. We use it here to
// demostrate the TypeScript test cabapbilities and compatibility
// with Hardhat 2.

// Uncomment this line to use console.log
// import "hardhat/console.sol";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@ pragma solidity ^0.8.24;
import "forge-std/Test.sol";

contract TestContract is Test {
ErrorsTest test;
BrokenContract test;

function setUp() public {
test = new ErrorsTest();
test = new BrokenContract();
}

function testExpectArithmetic() public {
function test_ExpectArithmeticError() public {
vm.expectRevert(stdError.arithmeticError);
test.arithmeticError(10);
test.forceArithmeticError(10);
}
}

contract ErrorsTest {
function arithmeticError(uint256 a) public pure returns (uint256) {
contract BrokenContract {
function forceArithmeticError(uint256 a) public pure returns (uint256) {
return a - 100;
}
}
91 changes: 84 additions & 7 deletions v-next/hardhat/templates/mocha-ethers/hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,109 @@
import { HardhatUserConfig } from "@ignored/hardhat-vnext/config";
import {
configVariable,
HardhatUserConfig,
} from "@ignored/hardhat-vnext/config";

import HardhatMochaTestRunner from "@ignored/hardhat-vnext-mocha-test-runner";
import HardhatEthers from "@ignored/hardhat-vnext-ethers";
import HardhatNetworkHelpers from "@ignored/hardhat-vnext-network-helpers";
import HardhatKeystore from "@ignored/hardhat-vnext-keystore";

const config: HardhatUserConfig = {
/*
* In Hardhat 3, plugins are defined as part of the Hardhat config instead of
* being based on the side-effect of imports.
*
* Note: A `hardhat-toolbox` like plugin for Hardhat 3 hasn't been defined yet,
* so this list is larger than what you would normally have.
*/
plugins: [
HardhatMochaTestRunner,
HardhatEthers,
HardhatNetworkHelpers,
HardhatKeystore,
],
solidity: {
version: "0.8.24",
/*
* Hardhat 3 supports different build profiles, allowing you to configure
* different versions of `solc` and its settings for various use cases.
*
* Note: Using profiles is optional, and any Hardhat 2 `solidity` config
* is still valid in Hardhat 3.
*/
profiles: {
/*
* The default profile is used when no profile is defined or specified
* in the CLI or by the tasks you are running.
*/
default: {
version: "0.8.24",
},
/*
* The production profile is meant to be used for deployments, providing
* more control over settings for production builds and taking some extra
* steps to simplify the process of verifying your contracts.
*/
production: {
version: "0.8.24",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
/*
* Hardhat 3 natively supports remappings and makes extensive use of them
* internally to fully support npm resolution rules (i.e., it supports
* transitive dependencies, multiple versions of the same package,
* monorepos, etc.).
*/
remappings: [
// This is necessary because most people import forge-std/Test.sol, and not forge-std/src/Test.sol.
// This will improve in the future to remove the need for a named version.
/*
* This remapping is added to the example because most people import
* forge-std/Test.sol, not forge-std/src/Test.sol.
*
* Note: The config currently leaks internal IDs, but this will be fixed
* in the future.
*/
"forge-std/=npm/[email protected]/src/",
],
},
/*
* The `networks` configuration is mostly compatible with Hardhat 2.
* The key differences right now are:
*
* - You must set a `type` for each network, which is either `http` or `edr`,
* allowing you to have multiple simulated networks.
*
* - You can set a `chainType` for each network, which is either `generic`,
* `l1`, or `optimism`. This has two uses. It ensures that you always
* connect to the network with the right Chain Type. And, on `edr`
* networks, it makes sure that the simulated chain behaves exactly like the
* real one. More information about this can be found in the test files.
*
* - Some config fields, like `forkConfig`, are different from Hardhat 2 and
* will be fixed in the near future.
*
* - The `accounts` field of `http` networks can also receive Configuration
* Variables, which are values that only get loaded when needed. This allows
* Hardhat to still run despite some of its config not being available
* (e.g., a missing private key or API key). More info about this can be
* found in the "Sending a Transaction to Optimism Sepolia" of the README.
*/
networks: {
edrOp: {
opSepolia: {
type: "http",
chainType: "optimism",
url: "https://sepolia.optimism.io/",
accounts: [configVariable("OPTIMISM_SEPOLIA_PRIVATE_KEY")],
},
edrOpSepolia: {
type: "edr",
chainId: 10,
chainType: "optimism",
forkConfig: {
jsonRpcUrl: "https://mainnet.optimism.io",
jsonRpcUrl: "https://sepolia.optimism.io",
},
},
},
Expand Down

This file was deleted.

48 changes: 48 additions & 0 deletions v-next/hardhat/templates/mocha-ethers/scripts/send-op-tx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { network } from "@ignored/hardhat-vnext";

// We connect to the default network (which can be controlled with `--network`),
// and use the `optimism` chain type.
const { ethers, networkConfig, networkName } = await network.connect(
undefined,
"optimism",
);

console.log("Sending transaction using network", networkName);

if (networkConfig.type === "edr") {
console.log("Using an EDR network simulating Optimism, forking it");
console.log(
"Note: The forking initialization is not optimized yet, and the example RPC is slower than usual.",
);
} else {
console.log("Using an HTTP connection to Optimism");
}

const [sender] = await ethers.getSigners();

console.log("Sender:", await sender.address);

console.log(
"Sender balance:",
await ethers.provider.getBalance(sender.address),
);

console.log("Sending 1 wei from", sender.address, "to itself");

console.log("Sending L2 transaction");
const tx = await sender.sendTransaction({
to: sender.address,
value: 1n,
});

const receipt = (await tx.wait())!;

console.log(
`Transaction included in block ${receipt.blockHash} (#${receipt.blockNumber})`,
);

if (networkName === "opSepolia") {
console.log(
`You can check your transaction on https://sepolia-optimism.etherscan.io/tx/${receipt.hash}`,
);
}
Loading

0 comments on commit bdf4a59

Please sign in to comment.