Skip to content

Commit de62281

Browse files
authored
Merge branch 'main' into chainlink
2 parents 6c251f3 + 2083738 commit de62281

31 files changed

+789
-344
lines changed

.env.example

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
KOVAN_RPC_URL='https://kovan.infura.io/v3/1234567890'
2-
RINKEBY_RPC_URL='https://alchemy.infura.io/v3/1234567890'
2+
RINKEBY_RPC_URL='https://rinkeby.infura.io/v3/1234567890'
33
POLYGON_MAINNET_RPC_URL='https://rpc-mainnet.maticvigil.com'
44
PRIVATE_KEY='abcdefg'
55
ALCHEMY_MAINNET_RPC_URL="https://eth-mainnet.alchemyapi.io/v2/your-api-key"
66
REPORT_GAS=true
77
COINMARKETCAP_API_KEY="YOUR_KEY"
8-
AUTO_FUND=true
8+
AUTO_FUND=true
9+
VRF_SUBSCRIPTION_ID=YOUR_SUBSCRIPTION_ID

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,6 @@ lint/outputs/
9898
lint/tmp/
9999
# lint/reports/
100100

101-
gas-report.txt
101+
gas-report.txt
102+
103+
contracts/test/fuzzing/crytic-export

.prettierrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"tabWidth": 2,
2+
"tabWidth": 4,
33
"useTabs": false,
44
"semi": false,
55
"singleQuote": false,

.solcover.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
skipFiles: ['test/fuzzing/KeepersCounterEchidnaTest.sol', 'test/LinkToken.sol', 'test/MockOracle.sol', 'test/MockV3Aggregator.sol', 'test/VRFCoordinatorV2Mock.sol'],
3+
};

README.md

Lines changed: 86 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,16 @@
88

99
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/smartcontractkit/hardhat-starter-kit)
1010

11-
12-
- [Chainlink Hardhat Box](#chainlink-hardhat-box)
11+
- [Chainlink Hardhat Starter Kit](#chainlink-hardhat-starter-kit)
1312
- [Getting Started](#getting-started)
1413
- [Requirements](#requirements)
1514
- [Quickstart](#quickstart)
1615
- [Typescript](#typescript)
17-
- [Useage](#useage)
16+
- [Usage](#usage)
1817
- [Deploying Contracts](#deploying-contracts)
1918
- [Run a Local Network](#run-a-local-network)
2019
- [Using a Testnet or Live Network (like Mainnet or Polygon)](#using-a-testnet-or-live-network-like-mainnet-or-polygon)
21-
- [Kovan Ethereum Testnet Setup](#kovan-ethereum-testnet-setup)
20+
- [Rinkeby Ethereum Testnet Setup](#rinkeby-ethereum-testnet-setup)
2221
- [Forking](#forking)
2322
- [Auto-Funding](#auto-funding)
2423
- [Test](#test)
@@ -32,11 +31,13 @@
3231
- [Linting](#linting)
3332
- [Code Formating](#code-formating)
3433
- [Estimaging Gas](#estimaging-gas)
34+
- [Code Coverage](#code-coverage)
35+
- [Fuzzing](#fuzzing)
3536
- [Contributing](#contributing)
3637
- [Thank You!](#thank-you)
3738
- [Resources](#resources)
3839

39-
# Chainlink Hardhat Box
40+
# Chainlink Hardhat Starter Kit
4041
Implementation of the following 4 Chainlink features using the [Hardhat](https://hardhat.org/) development environment:
4142
- [Chainlink Price Feeds](https://docs.chain.link/docs/using-chainlink-reference-contracts)
4243
- [Chainlink VRF](https://docs.chain.link/docs/chainlink-vrf)
@@ -59,6 +60,8 @@ It's recommended that you've gone through the [hardhat getting started documenta
5960
- `yarn --version` And get an output like: `x.x.x`
6061
- You might need to install it with npm
6162

63+
> If you're familiar with `npx` and `npm` instead of `yarn`, you can use `npx` for execution and `npm` for installing dependencies.
64+
6265
## Quickstart
6366

6467
1. Clone and install dependencies
@@ -88,7 +91,7 @@ yarn hardhat test
8891
or
8992

9093
```
91-
npx hardhat test
94+
yarn hardhat test
9295
```
9396

9497
### Typescript
@@ -100,7 +103,7 @@ git checkout typescript
100103
yarn
101104
```
102105

103-
# Useage
106+
# Usage
104107

105108
If you run `yarn hardhat --help` you'll get an output of all the tasks you can run.
106109

@@ -140,15 +143,15 @@ To interact with a live or test network, you'll need:
140143
2. A Private Key
141144
3. ETH & LINK token (either testnet or real)
142145

143-
Let's look at an example of setting these up using the Kovan testnet.
146+
Let's look at an example of setting these up using the Rinkeby testnet.
144147

145-
### Kovan Ethereum Testnet Setup
148+
### Rinkeby Ethereum Testnet Setup
146149

147150
First, we will need to set environment variables. We can do so by setting them in our `.env` file (create it if it's not there). You can also read more about [environment variables](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html) from the linked twilio blog. You'll find a sample of what this file will look like in `.env.example`
148151

149152
> IMPORTANT: MAKE SURE YOU'D DONT EXPOSE THE KEYS YOU PUT IN THIS `.env` FILE. By that, I mean don't push them to a public repo, and please try to keep them keys you use in development not associated with any real funds.
150153
151-
1. Set your `KOVAN_RPC_URL` [environment variable.](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html)
154+
1. Set your `RINKEBY_RPC_URL` [environment variable.](https://www.twilio.com/blog/2017/01/how-to-set-environment-variables.html)
152155

153156
You can get one for free from [Alchmey](https://www.alchemy.com/), [Infura](https://infura.io/), or [Moralis](https://moralis.io/speedy-nodes/). This is your connection to the blockchain.
154157

@@ -164,53 +167,60 @@ Don't commit and push any changes to .env files that may contain sensitive infor
164167

165168
`.env` example:
166169
```
167-
KOVAN_RPC_URL='www.infura.io/asdfadsfafdadf'
170+
RINKEBY_RPC_URL='www.infura.io/asdfadsfafdadf'
168171
PRIVATE_KEY='abcdef'
169172
```
170173
`bash` example
171174
```
172-
export KOVAN_RPC_URL='www.infura.io/asdfadsfafdadf'
175+
export RINKEBY_RPC_URL='www.infura.io/asdfadsfafdadf'
173176
export PRIVATE_KEY='abcdef'
174177
```
175178

176179
> You can also use a `MNEMONIC` instead of a `PRIVATE_KEY` environment variable by uncommenting the section in the `hardhat.config.js`, and commenting out the `PRIVATE_KEY` line. However this is not recommended.
177180
178181
For other networks like mainnet and polygon, you can use different environment variables for your RPC URL and your private key. See the `hardhat.config.js` to learn more.
179182

180-
3. Get some Kovan Testnet ETH and LINK
183+
3. Get some Rinkeby Testnet ETH and LINK
181184

182185
Head over to the [Chainlink faucets](https://faucets.chain.link/) and get some ETH and LINK. Please follow [the chainlink documentation](https://docs.chain.link/docs/acquire-link/) if unfamiliar.
183186

184-
4. Running commands
187+
4. Create VRF V2 subscription
188+
189+
Head over to [VRF Subscription Page](https://vrf.chain.link/rinkeby) and create the new subscription. Save your subscription ID and put it in `.env` file as `VRF_SUBSCRIPTION_ID`
190+
191+
5. Running commands
185192

186-
You should now be all setup! You can run any command and just pass the `--network kovan` now!
193+
You should now be all setup! You can run any command and just pass the `--network rinkeby` now!
187194

188195
To deploy contracts:
189196

190197
```
191-
yarn hardhat deploy --network kovan
198+
yarn hardhat deploy --network rinkeby
192199
```
193200

194201
To run staging testnet tests
195202
```
196-
yarn hardhat test --network kovan
203+
yarn hardhat test --network rinkeby
197204
```
198205

199206
## Forking
200207

201208
If you'd like to run tests or on a network that is a [forked network](https://hardhat.org/hardhat-network/guides/mainnet-forking.html)
202209
1. Set a `MAINNET_RPC_URL` environment variable that connects to the mainnet.
203-
2. Uncomment the section in your `hardhat.config.js`
210+
2. Choose a block number to select a state of the network you are forking and set it as `FORKING_BLOCK_NUMBER` environment variable. If ignored, it will use the latest block each time which can lead to test inconsistency.
211+
3. Set `enabled` flag to `true`/`false` to enable/disable forking feature
204212
```
205-
// forking: {
206-
// url: MAINNET_RPC_URL
207-
// }
213+
forking: {
214+
url: MAINNET_RPC_URL,
215+
blockNumber: FORKING_BLOCK_NUMBER,
216+
enabled: false,
217+
}
208218
```
209219

210220

211221
## Auto-Funding
212222

213-
This Starter Kit is configured by default to attempt to auto-fund any newly deployed contract that uses Any-API or Chainlink VRF, to save having to manually fund them after each deployment. The amount in LINK to send as part of this process can be modified in the [Starter Kit Config](helper-hardhat-config.js), and are configurable per network.
223+
This Starter Kit is configured by default to attempt to auto-fund any newly deployed contract that uses Any-API, to save having to manually fund them after each deployment. The amount in LINK to send as part of this process can be modified in the [Starter Kit Config](helper-hardhat-config.js), and are configurable per network.
214224

215225
| Parameter | Description | Default Value |
216226
| ---------- | :------------------------------------------------ | :------------ |
@@ -227,7 +237,7 @@ To run unit tests:
227237
```bash
228238
yarn test
229239
```
230-
Or
240+
or
231241
```
232242
yarn hardhat test
233243
```
@@ -241,13 +251,25 @@ yarn test-integration
241251
or
242252

243253
```
244-
yarn hardhat test --network kovan
254+
yarn hardhat test --network rinkeby
255+
```
256+
257+
## Performance optimizations
258+
259+
Since all tests are written in a way to be independent from each other, you can save time by running them in parallel. Make sure that `AUTO_FUND=false` inside `.env` file. There are some limitations with parallel testing, read more about them [here](https://hardhat.org/guides/parallel-tests.html)
260+
261+
To run tests in parallel:
262+
```
263+
yarn test --parallel
264+
```
265+
or
266+
```
267+
yarn hardhat test --parallel
245268
```
246269

247270
# Interacting with Deployed Contracts
248271

249-
After deploying your contracts.
250-
The deployment output will give you the contract addresses as they are deployed. You can then use these contract addresses in conjunction with Hardhat tasks to perform operations on each contract.
272+
After deploying your contracts, the deployment output will give you the contract addresses as they are deployed. You can then use these contract addresses in conjunction with Hardhat tasks to perform operations on each contract.
251273

252274

253275
## Chainlink Price Feeds
@@ -277,13 +299,19 @@ yarn hardhat read-data --contract insert-contract-address-here --network network
277299

278300

279301
## VRF Get a random number
280-
The VRFConsumer contract has two tasks, one to request a random number, and one to read the result of the random number request. This contract needs to be funded with link first:
302+
The VRFConsumer contract has two tasks, one to request a random number, and one to read the result of the random number request. To start, go to [VRF Subscription Page](https://vrf.chain.link/rinkeby) and create the new subscription. Save your subscription ID and put it in `.env` file as `VRF_SUBSCRIPTION_ID`:
281303

282304
```bash
283-
yarn hardhat fund-link --contract insert-contract-address-here --network network
305+
VRF_SUBSCRIPTION_ID=subscription_id
284306
```
285307

286-
Once it's funded, you can perform a VRF request with the request-random-number task:
308+
Then, deploy your VRF V2 contract consumer to the network of your recent subscription using subscription id as constructor argument.
309+
310+
```bash
311+
yarn hardhat deploy --network network
312+
```
313+
314+
Finally, you need to go to your subscription page one more time and add the address of deployed contract as a new consumer. Once that's done, you can perform a VRF request with the request-random-number task:
287315

288316
```bash
289317
yarn hardhat request-random-number --contract insert-contract-address-here --network network
@@ -313,7 +341,7 @@ yarn hardhat verify --network <NETWORK> <CONTRACT_ADDRESS> <CONSTRUCTOR_PARAMETE
313341
example:
314342

315343
```
316-
yarn hardhat verify --network kovan 0x9279791897f112a41FfDa267ff7DbBC46b96c296 "0x9326BFA02ADD2366b30bacB125260Af641031331"
344+
yarn hardhat verify --network rinkeby 0x9279791897f112a41FfDa267ff7DbBC46b96c296 "0x9326BFA02ADD2366b30bacB125260Af641031331"
317345
```
318346

319347
# View Contracts Size
@@ -348,6 +376,34 @@ yarn hardhat test
348376

349377
If you'd like to see the gas prices in USD or other currency, add a `COINMARKETCAP_API_KEY` from [Coinmarketcap](https://coinmarketcap.com/api/documentation/v1/).
350378

379+
# Code coverage
380+
381+
To see a measure in percent of the degree to which the smart contract source code is executed when a particular test suite is run, type
382+
```
383+
yarn coverage
384+
```
385+
386+
# Fuzzing
387+
388+
We are going to use Echidna as a Fuzz testing tool. You need to have [Docker](https://www.docker.com/) installed with at least 8GB virtual memory allocated (To update this parameter go to _Settings->Resources->Advanced->Memory_).
389+
390+
To start Echidna instance run
391+
392+
```
393+
yarn fuzzing
394+
```
395+
396+
If you are using it for the first time, you will need to wait for Docker to download [eth-security-toolbox](https://hub.docker.com/r/trailofbits/eth-security-toolbox) image for us.
397+
398+
To start Fuzzing run
399+
```
400+
echidna-test /src/contracts/test/fuzzing/KeepersCounterEchidnaTest.sol --contract KeepersCounterEchidnaTest --config /src/contracts/test/fuzzing/config.yaml
401+
```
402+
403+
To exit Echidna type
404+
```bash
405+
exit
406+
```
351407

352408
# Contributing
353409

contracts/APIConsumer.sol

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,56 @@
1+
// SPDX-License-Identifier: MIT
12
pragma solidity ^0.8.7;
23

34
import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
45

56
/**
6-
* THIS IS AN EXAMPLE CONTRACT WHICH USES HARDCODED VALUES FOR CLARITY.
7-
* PLEASE DO NOT USE THIS CODE IN PRODUCTION.
7+
* @title The APIConsumer contract
8+
* @notice An API Consumer contract that makes GET requests to obtain 24h trading volume of ETH in USD
89
*/
910
contract APIConsumer is ChainlinkClient {
1011
using Chainlink for Chainlink.Request;
1112

1213
uint256 public volume;
13-
address private oracle;
14-
bytes32 private jobId;
15-
uint256 private fee;
14+
address private immutable oracle;
15+
bytes32 private immutable jobId;
16+
uint256 private immutable fee;
1617

1718
event DataFullfilled(uint256 volume);
1819

1920
/**
20-
* Network: Kovan
21-
* Oracle: 0xc57B33452b4F7BB189bB5AfaE9cc4aBa1f7a4FD8 (Chainlink Devrel
22-
* Node)
23-
* Job ID: d5270d1c311941d0b08bead21fea7747
21+
* @notice Executes once when a contract is created to initialize state variables
22+
*
23+
* @param _oracle - address of the specific Chainlink node that a contract makes an API call from
24+
* @param _jobId - specific job for :_oracle: to run; each job is unique and returns different types of data
25+
* @param _fee - node operator price per API call / data request
26+
* @param _link - LINK token address on the corresponding network
27+
*
28+
* Network: Rinkeby
29+
* Oracle: 0xc57b33452b4f7bb189bb5afae9cc4aba1f7a4fd8
30+
* Job ID: 6b88e0402e5d415eb946e528b8e0c7ba
2431
* Fee: 0.1 LINK
2532
*/
2633
constructor(
2734
address _oracle,
2835
bytes32 _jobId,
2936
uint256 _fee,
3037
address _link
31-
) public {
38+
) {
3239
if (_link == address(0)) {
3340
setPublicChainlinkToken();
3441
} else {
3542
setChainlinkToken(_link);
3643
}
37-
// oracle = 0x2f90A6D021db21e1B2A077c5a37B3C7E75D15b7e;
38-
// jobId = "29fa9aa13bf1468788b7cc4a500a45b8";
39-
// fee = 0.1 * 10 ** 18; // 0.1 LINK
4044
oracle = _oracle;
4145
jobId = _jobId;
4246
fee = _fee;
4347
}
4448

4549
/**
46-
* Create a Chainlink request to retrieve API response, find the target
50+
* @notice Creates a Chainlink request to retrieve API response, find the target
4751
* data, then multiply by 1000000000000000000 (to remove decimal places from data).
52+
*
53+
* @return requestId - id of the request
4854
*/
4955
function requestVolumeData() public returns (bytes32 requestId) {
5056
Chainlink.Request memory request = buildChainlinkRequest(
@@ -66,7 +72,8 @@ contract APIConsumer is ChainlinkClient {
6672
// }
6773
// }
6874
// }
69-
request.add("path", "RAW.ETH.USD.VOLUME24HOUR");
75+
// request.add("path", "RAW.ETH.USD.VOLUME24HOUR"); // Chainlink nodes prior to 1.0.0 support this format
76+
request.add("path", "RAW,ETH,USD,VOLUME24HOUR"); // Chainlink nodes 1.0.0 and later support this format
7077

7178
// Multiply the result by 1000000000000000000 to remove decimals
7279
int256 timesAmount = 10**18;
@@ -77,7 +84,10 @@ contract APIConsumer is ChainlinkClient {
7784
}
7885

7986
/**
80-
* Receive the response in the form of uint256
87+
* @notice Receives the response in the form of uint256
88+
*
89+
* @param _requestId - id of the request
90+
* @param _volume - response; requested 24h trading volume of ETH in USD
8191
*/
8292
function fulfill(bytes32 _requestId, uint256 _volume)
8393
public
@@ -86,4 +96,10 @@ contract APIConsumer is ChainlinkClient {
8696
volume = _volume;
8797
emit DataFullfilled(volume);
8898
}
99+
100+
/**
101+
* @notice Witdraws LINK from the contract
102+
* @dev Implement a withdraw function to avoid locking your LINK in the contract
103+
*/
104+
function withdrawLink() external {}
89105
}

0 commit comments

Comments
 (0)