Skip to content

Commit f5ed5e5

Browse files
authored
Merge pull request #548 from sc-forks/hardhat
Add Hardhat support
2 parents 6761556 + e106076 commit f5ed5e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2450
-1896
lines changed

.circleci/config.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,26 @@ jobs:
6565
executor: win/default
6666
steps:
6767
- checkout
68-
- run: dotnet tool install --global PowerShell
6968
- run:
7069
name: Windows Metacoin E2E
7170
command: |
7271
bash ./scripts/run-metacoin.sh
73-
e2e-buidler:
72+
e2e-nomiclabs:
7473
machine: true
7574
steps:
7675
- checkout
7776
- <<: *step_install_nvm
7877
- run:
79-
name: Buidler E2E
78+
name: Buidler & Hardhat E2E
8079
command: |
81-
./scripts/run-buidler.sh
80+
./scripts/run-nomiclabs.sh
8281
workflows:
8382
version: 2
8483
build:
8584
jobs:
8685
- unit-test
87-
- e2e-zeppelin
86+
# Temporarily disabled due to unskipped GSN gas measurement tests
87+
# - e2e-zeppelin
8888
- e2e-metacoin
8989
- e2e-metacoin-windows
90-
- e2e-buidler
90+
- e2e-nomiclabs

HARDHAT_README.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
[![Gitter chat](https://badges.gitter.im/sc-forks/solidity-coverage.svg)][18]
2+
![npm (tag)](https://img.shields.io/npm/v/solidity-coverage/latest)
3+
[![CircleCI](https://circleci.com/gh/sc-forks/solidity-coverage.svg?style=svg)][20]
4+
[![codecov](https://codecov.io/gh/sc-forks/solidity-coverage/branch/beta/graph/badge.svg)][21]
5+
[![hardhat](https://hardhat.org/buidler-plugin-badge.svg?1)][26]
6+
7+
# solidity-coverage
8+
9+
Solidity code coverage plugin for [Hardhat](http://hardhat.org).
10+
11+
## What
12+
13+
![coverage example][22]
14+
15+
+ For more details about how it works and potential limitations, see [the accompanying article][16].
16+
+ `solidity-coverage` is also [JoinColony/solcover][17]
17+
18+
19+
## Installation
20+
21+
```bash
22+
$ npm install --save-dev solidity-coverage
23+
```
24+
25+
And add the following to your `.config.js`:
26+
27+
```js
28+
require("solidity-coverage");
29+
```
30+
31+
Or, if you are using TypeScript, add this to your hardhat.config.ts:
32+
33+
```ts
34+
import "solidity-coverage"
35+
```
36+
37+
## Tasks
38+
39+
This plugin implements a `coverage` task
40+
41+
```bash
42+
npx hardhat coverage [options]
43+
```
44+
45+
| Option <img width=200/> | Example <img width=750/>| Description <img width=1000/> |
46+
|--------------|------------------------------------|--------------------------------|
47+
| testfiles | `--testfiles "test/registry/*.ts"` | Test file(s) to run. (Globs must be enclosed by quotes.)|
48+
| solcoverjs | `--solcoverjs ./../.solcover.js` | Relative path from working directory to config. Useful for monorepo packages that share settings. (Path must be "./" prefixed) |
49+
| network | `--network development` | Run with a ganache client over http using network settings defined in `hardhat.config.js`. (Hardhat is the default network) |
50+
51+
52+
## Configuration
53+
54+
Options can be specified in a `.solcover.js` config file located in the root directory of your project.
55+
56+
**Config Example:**
57+
```javascript
58+
module.exports = {
59+
skipFiles: ['Routers/EtherRouter.sol']
60+
};
61+
```
62+
63+
| Option <img width=200/>| Type <img width=200/> | Default <img width=1300/> | Description <img width=800/> |
64+
| ------ | ---- | ------- | ----------- |
65+
| silent | *Boolean* | false | Suppress logging output |
66+
| client | *Object* | undefined | *Ganache only*: Useful if you need a specific ganache version. An example value is: `require('ganache-cli')` |
67+
| providerOptions | *Object* | `{ }` | *Ganache only*: [ganache-core options][1] |
68+
| skipFiles | *Array* | `['Migrations.sol']` | Array of contracts or folders (with paths expressed relative to the `contracts` directory) that should be skipped when doing instrumentation. |
69+
| istanbulFolder | *String* | `./coverage` | Folder location for Istanbul coverage reports. |
70+
| istanbulReporter | *Array* | `['html', 'lcov', 'text', 'json']` | [Istanbul coverage reporters][2] |
71+
| mocha | *Object* | `{ }` | [Mocha options][3] to merge into existing mocha config. `grep` and `invert` are useful for skipping certain tests under coverage using tags in the test descriptions.|
72+
| onServerReady[<sup>*</sup>][14] | *Function* | | Hook run *after* server is launched, *before* the tests execute. Useful if you need to use the Oraclize bridge or have setup scripts which rely on the server's availability. [More...][23] |
73+
| onCompileComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* compilation completes, *before* tests are run. Useful if you have secondary compilation steps or need to modify built artifacts. [More...][23]|
74+
| onTestsComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the tests complete, *before* Istanbul reports are generated. [More...][23]|
75+
| onIstanbulComplete[<sup>*</sup>][14] | *Function* | | Hook run *after* the Istanbul reports are generated, *before* the ganache server is shut down. Useful if you need to clean resources up. [More...][23]|
76+
77+
[<sup>*</sup> Advanced use][14]
78+
79+
## Usage
80+
81+
+ Coverage runs tests a little more slowly.
82+
+ Coverage uses the Hardhat network by default.
83+
+ Coverage [distorts gas consumption][13]. Tests that check exact gas consumption should be [skipped][24].
84+
+ :warning: Contracts are compiled **without optimization**. Please report unexpected compilation faults to [issue 417][25]
85+
86+
## Using with ganache
87+
88+
Begining with `v0.7.12`, this plugin runs directly on the Hardhat network by default (for speed).
89+
90+
If you want to use a ganache based http network, you can specify it by name using the `--network` cli option. The plugin will then launch its own coverage enabled ganache instance which can be configured in `.solcover.js` via the `providerOptions` key.
91+
92+
## Documentation
93+
94+
More documentation, including FAQ and information about solidity-coverage's API [is available here][28].
95+
96+
97+
[1]: https://github.com/trufflesuite/ganache-core#options
98+
[2]: https://istanbul.js.org/docs/advanced/alternative-reporters/
99+
[3]: https://mochajs.org/api/mocha
100+
[4]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-gas
101+
[5]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-memory
102+
[6]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#running-out-of-time
103+
[7]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#continuous-integration
104+
[8]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-branch-coverage
105+
[9]: https://sc-forks.github.io/metacoin/
106+
[10]: https://coveralls.io/github/OpenZeppelin/openzeppelin-solidity?branch=master
107+
[11]: https://github.com/sc-forks/solidity-coverage/tree/master/test/units
108+
[12]: https://github.com/sc-forks/solidity-coverage/issues
109+
[13]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/faq.md#notes-on-gas-distortion
110+
[14]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md
111+
[15]: #config-options
112+
[16]: https://blog.colony.io/code-coverage-for-solidity-eecfa88668c2
113+
[17]: https://github.com/JoinColony/solcover
114+
[18]: https://gitter.im/sc-forks/solidity-coverage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
115+
[19]: https://badge.fury.io/js/solidity-coverage
116+
[20]: https://circleci.com/gh/sc-forks/solidity-coverage
117+
[21]: https://codecov.io/gh/sc-forks/solidity-coverage
118+
[22]: https://cdn-images-1.medium.com/max/800/1*uum8t-31bUaa6dTRVVhj6w.png
119+
[23]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#workflow-hooks
120+
[24]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#skipping-tests
121+
[25]: https://github.com/sc-forks/solidity-coverage/issues/417
122+
[26]: https://hardhat.org/
123+
[27]: https://www.trufflesuite.com/docs
124+
[28]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/api.md
125+
[29]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/upgrade.md#upgrading-from-06x-to-070
126+
[30]: https://github.com/sc-forks/solidity-coverage/tree/0.6.x-final#solidity-coverage
127+
[31]: https://github.com/sc-forks/solidity-coverage/releases/tag/v0.7.0
128+
[32]: https://github.com/sc-forks/buidler-e2e/tree/coverage
129+
[33]: https://github.com/sc-forks/moloch
130+
[34]: https://github.com/sc-forks/solidity-coverage/blob/master/docs/advanced.md#reducing-the-instrumentation-footprint
131+

lib/api.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ class API {
4949
this.skipFiles = config.skipFiles || [];
5050

5151
this.log = config.log || console.log;
52-
53-
this.gasLimit = 0xffffffffff; // default "gas sent" with transactions
54-
this.gasLimitString = "0xfffffffffff"; // block gas limit for ganache (higher than "gas sent")
52+
this.gasLimit = 0xffffffffff // default "gas sent" with transactions
53+
this.gasLimitString = "0x1fffffffffffff"; // block gas limit for ganache (higher than "gas sent")
54+
this.gasLimitNumber = 0x1fffffffffffff; // block gas limit for Hardhat
5555
this.gasPrice = 0x01;
5656

5757
this.istanbulFolder = config.istanbulFolder || false;
@@ -158,14 +158,14 @@ class API {
158158
// Attach to vm step of supplied client
159159
try {
160160
if (this.config.forceBackupServer) throw new Error()
161-
await this.attachToVM(client)
161+
await this.attachToGanacheVM(client)
162162
}
163163

164164
// Fallback to ganache-cli)
165165
catch(err) {
166166
const _ganache = require('ganache-cli');
167167
this.ui.report('vm-fail', [_ganache.version]);
168-
await this.attachToVM(_ganache);
168+
await this.attachToGanacheVM(_ganache);
169169
}
170170

171171
if (autoLaunchServer === false || this.autoLaunchServer === false){
@@ -228,7 +228,7 @@ class API {
228228
// ========
229229
// Provider
230230
// ========
231-
async attachToVM(client){
231+
async attachToGanacheVM(client){
232232
const self = this;
233233

234234
// Fallback to client from options
@@ -268,6 +268,31 @@ class API {
268268
})
269269
}
270270

271+
// Hardhat
272+
attachToHardhatVM(provider){
273+
const self = this;
274+
this.collector = new DataCollector(this.instrumenter.instrumentationData);
275+
276+
let cur = provider;
277+
278+
// Go down to core HardhatNetworkProvider
279+
while (cur._wrapped) {
280+
cur = Object.assign({}, cur._wrapped)
281+
}
282+
cur._node._vm.on('step', self.collector.step.bind(self.collector))
283+
}
284+
285+
// Temporarily disabled because some relevant traces aren't available
286+
// (maybe bytecode cannot be found)
287+
/*hardhatTraceHandler(trace, isTraceFromCall){
288+
for (const step of trace.steps){
289+
if (trace.bytecode && trace.bytecode._pcToInstruction){
290+
const instruction = trace.bytecode._pcToInstruction.get(step.pc)
291+
this.collector.trackHardhatEVMInstruction(instruction)
292+
}
293+
}
294+
}*/
295+
271296
// ========
272297
// File I/O
273298
// ========

lib/collector.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,35 @@ class DataCollector {
2323
if (this.validOpcodes[info.opcode.name] && info.stack.length > 0){
2424
const idx = info.stack.length - 1;
2525
let hash = web3Utils.toHex(info.stack[idx]).toString();
26-
hash = this._normalizeHash(hash);
27-
28-
if(this.instrumentationData[hash]){
29-
this.instrumentationData[hash].hits++;
30-
}
26+
this._registerHash(hash)
3127
}
3228
} catch (err) { /*Ignore*/ };
3329
}
3430

31+
// Temporarily disabled because some relevant traces aren't available
32+
/**
33+
* Converts pushData value to string and registers in instrumentation map.
34+
* @param {HardhatEVMTraceInstruction} instruction
35+
*/
36+
/*trackHardhatEVMInstruction(instruction){
37+
if (instruction && instruction.pushData){
38+
let hash = `0x` + instruction.pushData.toString('hex');
39+
this._registerHash(hash)
40+
}
41+
}*/
42+
43+
/**
44+
* Normalizes has string and marks hit.
45+
* @param {String} hash bytes32 hash
46+
*/
47+
_registerHash(hash){
48+
hash = this._normalizeHash(hash);
49+
50+
if(this.instrumentationData[hash]){
51+
this.instrumentationData[hash].hits++;
52+
}
53+
}
54+
3555
/**
3656
* Left-pads zero prefixed bytes 32 hashes to length 66. The '59' in the
3757
* comparison below is arbitrary. It provides a margin for recurring zeros

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "solidity-coverage",
33
"version": "0.7.11",
44
"description": "",
5-
"main": "plugins/buidler.plugin.js",
5+
"main": "plugins/nomiclabs.plugin.js",
66
"bin": {
77
"solidity-coverage": "./plugins/bin.js"
88
},
@@ -41,15 +41,19 @@
4141
"recursive-readdir": "^2.2.2",
4242
"sc-istanbul": "^0.4.5",
4343
"shelljs": "^0.8.3",
44-
"web3": "^1.3.0"
44+
"web3-utils": "^1.3.0"
4545
},
4646
"devDependencies": {
4747
"@nomiclabs/buidler": "^1.3.6",
4848
"@nomiclabs/buidler-truffle5": "^1.3.4",
4949
"@nomiclabs/buidler-web3": "^1.3.4",
50+
"@nomiclabs/hardhat-truffle5": "^2.0.0",
51+
"@nomiclabs/hardhat-web3": "^2.0.0",
5052
"@truffle/contract": "^4.0.36",
5153
"buidler-gas-reporter": "^0.1.3",
5254
"decache": "^4.5.1",
55+
"hardhat": "^2.0.2",
56+
"hardhat-gas-reporter": "^1.0.1",
5357
"mocha": "5.2.0",
5458
"nyc": "^14.1.1",
5559
"solc": "^0.5.10",

plugins/buidler.plugin.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
const API = require('./../lib/api');
22
const utils = require('./resources/plugin.utils');
3-
const buidlerUtils = require('./resources/buidler.utils');
4-
const PluginUI = require('./resources/buidler.ui');
3+
const buidlerUtils = require('./resources/nomiclabs.utils');
4+
const PluginUI = require('./resources/nomiclabs.ui');
55

66
const pkg = require('./../package.json');
77
const death = require('death');
88
const path = require('path');
9-
const Web3 = require('web3');
109

1110
const { task, types } = require("@nomiclabs/buidler/config");
1211
const { ensurePluginLoadedWithUsePlugin } = require("@nomiclabs/buidler/plugins");
@@ -53,25 +52,24 @@ function plugin() {
5352
// ==============
5453
// Server launch
5554
// ==============
56-
const network = buidlerUtils.setupNetwork(env, api, ui);
55+
const network = buidlerUtils.setupBuidlerNetwork(env, api, ui);
5756

5857
const client = api.client || require('ganache-cli');
5958
const address = await api.ganache(client);
60-
const web3 = new Web3(address);
61-
const accounts = await web3.eth.getAccounts();
62-
const nodeInfo = await web3.eth.getNodeInfo();
63-
const ganacheVersion = nodeInfo.split('/')[1];
59+
const accountsRequest = await utils.getAccountsGanache(api.server.provider);
60+
const nodeInfoRequest = await utils.getNodeInfoGanache(api.server.provider);
61+
const ganacheVersion = nodeInfoRequest.result.split('/')[1];
6462

6563
// Set default account
66-
network.from = accounts[0];
64+
network.from = accountsRequest.result[0];
6765

6866
// Version Info
6967
ui.report('versions', [
7068
ganacheVersion,
7169
pkg.version
7270
]);
7371

74-
ui.report('network', [
72+
ui.report('ganache-network', [
7573
env.network.name,
7674
api.port
7775
]);

0 commit comments

Comments
 (0)