Hardhat reef plugin allows to deploy and interact with contracts through hardhat and reef.js.
yarn add @reef-defi/hardhat-reef
Reef network can be configured in hardhat.config.js
file.
Here is an example of a configuration for both localhost reef-node
and reef-testnet
.
To inject your account insert account mnemonic in <INSERT ACCOUNT MNEMONIC SEED>
field and then access it through await hre.reef.getSignerByName("Account1");
.
module.exports = {
solidity: "0.8.4",
networks: {
// Mainnet reef network configuration
reef_mainnet: {
url: "wss://rpc.reefscan.com/ws",
scanUrl: "https://reefscan.com"
seeds: {
"MyAccount1": "<INSERT ACCOUNT MNEMONIC SEED>",
}
},
// Testnet reef network configuration
reef_testnet: {
url: "wss://rpc-testnet.reefscan.com/ws",
scanUrl: "https://reefscan.com"
seeds: {
"MyAccount1": "<INSERT ACCOUNT MNEMONIC SEED>",
}
},
// Localhost reef network configuration
reef: {
url: "ws://127.0.0.1:9944",
scanUrl: "http://localhost:3000"
seeds: {
"MyAccount1": "<INSERT ACCOUNT MNEMONIC SEED>",
}
},
},
};
Each network configuration accepts
url
: used to connect to the desired network.seeds
: used to injecting a real account in the script. The default value is set to{}
.
Default values for both networks are set with the URLs shown in the example. This way system works straight out of the box without the need for configuration. Therefore configuration is only required if the user wants to change the URL of a network or if he wants to inject his accounts.
The plugin exposes hre.reef
object for interaction with the reef compatible chain.
See examples repo for examples of usage.
Make sure to claim the EVM account before making any other EVM calls:
const myAccount = await hre.reef.getSignerByName("myAccount");
await myAccount.claimDefaultAccount();
This has to be done only once, multiple calls however won't change anything.
Get signers all signers.
const signers = await hre.reef.getSigners();
const [signer1] = await hre.reef.getSigners();
Get signer with address.
const address = "0x0000" // Address needs to be changed
const singer = await hre.reef.getSigner(address);
Access signers address and find it again.
const [signer1] = await hre.reef.getSigners();
const address = await signer1.getAddress();
const previousSigner = await hre.reef.getSigner(address);
console.log(address === await previousSigner.getAddress());
Get signer by name.
Available localhost test signers: [alice, bob, charlie, dave, eve, ferdie]
.
const signer = await hre.reef.getSignerByName("alice");
Access injected account from configuration.
const signer = await hre.reef.gerSignerByName("Account name used in config file");
Creating contract factory.
const Flipper = await hre.reef.getContractFactory("Flipper");
Creating contract factory with arguments.
const Flipper = await hre.reef.getContractFactory("Flipper");
const flipper = await Flipper.deploy(false);
Creating contract factory with explicit signer.
const alice = await hre.reef.getSignerByName("alice");
const Flipper = await hre.reef.getContractFactory("Flipper", alice);
const flipper = await Flipper.deploy(true);
Finding flipper contract through his address.
const Flipper = await hre.reef.getContractFactory("Flipper", alice);
const flipper = await Flipper.deploy(false);
const address = await flipper.address;
const contract = await hre.reef.getContractAt(Flipper, address);
In this example we will show how to create a new project, test, deploy and interact with the contract. We will use Hardhat Reef Template. The template provides us with a Greeter contract, some scripts, all necessary dependencies, and hardhat configurations for the Reef node.
There are two simple ways to start new project.
- Clone Hardhat Reef Template.
git clone git@github.com:reef-defi/hardhat-reef-template.git greeter
cd greeter
yarn
- Use Hardhat Reef Template to configure new project on Github and then clone it.
git clone git@github.com:/username/greeter.git
cd greeter
yarn
First, we need to configure our account from the Reef testnet.
Injected account will be used to deploy the contract on the chain.
This can easily be done in hardhat.config.js
file by replacing <INSERT ACCOUNT MNEMONIC SEED>
with account mnemonic seed.
Make sure account hold suffitient amount of reef tokens!
If you are going to use the localhost chain, run it by hand and transfer some funds from alice
test account.
module.exports = {
solidity: "0.8.0",
networks: {
reef_mainnet: {
url: "wss://rpc.reefscan.com/ws",
seeds: {
"MyAccount": "<INSERT ACCOUNT MNEMONIC SEED>"
}
},
reef_testnet: {
url: "wss://rpc-testnet.reefscan.com/ws",
seeds: {
"MyAccount": "<INSERT ACCOUNT MNEMONIC SEED>"
}
},
reef: {
url: "ws://127.0.0.1:9944",
seeds: {
"MyAccount": "<INSERT ACCOUNT MNEMONIC SEED>"
}
},
},
};
All the contracts must be written under contracts/
folder.
Contract truffle tests are located under test/
folder.
All the tests will be run on hardhat network!
Running tests: yarn hardhat test
To deploy a contract we need to write a script scripts/deploy.js
. The script will read the Greeter contract and deploy it with an injected account.
const hre = require("hardhat");
async function main() {
// accessing injected account
const signer = await hre.reef.getSignerByName("MyAccount");
// retreaving Greeter contract factory with injected account
const Greeter = await hre.reef.getContractFactory("Greeter", signer);
// deploying the contract with inital "Hello world"
const greeter = await Greeter.deploy("Hello world");
// waiting for the contract to be deployed
await greeter.deployed();
// Make sure to print out the contract address and save it!
console.log(`Greeter contract address: ${greeter.address}`);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Save the contract address after the script is finished!
Run deployment script on the Local reef network: yarn hardhat run script/deploy.js
.
Run deployment script on the Reef test network: yarn hardhat run script/deploy.js --network reef_testnet
.
Run deployment script on the Hardhat network: yarn hardhat run script/deploy.js --network hardhat
.
We will create a new script scirpts/interact.js
to interact with the Greeter contract.
The script will retrieve the Greeter contract from the chain, read its current greeting value, change the greeting text, and read it again.
const hre = require("hardhat");
async function main() {
// copy the Greeter contract address from deployment script
const contractAddress = "INSERT CONTRACT ADDRESS";
// accessing injected account
const signer = await hre.reef.getSignerByName("MyAccount");
// retrieve Greeter contract from chain
const greeter = await hre.reef.getContractAt("Greeter", contractAddress, signer);
// now we can access existing contract values
console.log("Current greeter text: ", await greeter.greet());
// modify it
await greeter.setGreeting("Greetings!");
// and view it again
console.log("New greeter text: ", await greeter.greet());
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Run interaction script for the Local reet network: yarn hardhat run script/interact.js
.
Run interaction script for the Reef test network: yarn hardhat run script/interact.js --network reef_testnet
.
Hardhat reef also supports verifying contract from the Reef chain! Here is a basic example of contract deploymant and verification combined:
const hre = require("hardhat");
const wait = async (ms) => new Promise((res) => setTimeout(res, ms));
async function main() {
const signer = await hre.reef.getSignerByName("Acc");
const ERC20Contract = await hre.reef.getContractFactory("ERC20Contract", signer);
const args = ["180000000000000000000000000000000000"];
const erc20CContract = await ERC20Contract.deploy(...args);
await erc20CContract.deployed();
await hre.reef.verifyContract(erc20CContract.address, "ERC20Contract", args);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
If your project uses multiple compiler versions it will be necessary to specify compiler versions to verify the contract. Users can therefore specify partial compiler config containing:
- compilerVersion: specific compiler version in format
v0.8.4+commit.c7e474f2
- optimization: boolean flag that indicates if contracts were optimized
- runs: specific number of runs that were used when contract was compiled
- target: specific compiler target ['london', 'berlin', ...]
Here is one example how to use it. We will create the same contract as before, but here we will specify all compiler options.
const hre = require("hardhat");
const wait = async (ms) => new Promise((res) => setTimeout(res, ms));
async function main() {
const signer = await hre.reef.getSignerByName("Acc");
const ERC20Contract = await hre.reef.getContractFactory("ERC20Contract", signer);
const args = ["180000000000000000000000000000000000"];
const erc20CContract = await ERC20Contract.deploy(...args);
await erc20CContract.deployed();
await hre.reef.verifyContract(
erc20CContract.address,
"ERC20Contract",
args,
{
runs: 200, // We are still placing runs event tho optimization is set to false
target: 'london', // Default target version
optimization: false, // We are not using contract optimization
compilerVersion: "v0.8.4+commit.c7e474f2", // Examples project uses 0.8.4 solidity compiler version
}
);
}
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Running yarn test
will run every test located in the test/
folder. They
use mocha and chai.
We recommend creating unit tests for your own modules, and integration tests for the interaction of the plugin with Hardhat and its dependencies.
In folder test/fixture-projects/hardhat-project
there is an example project, which can be used to test deployment on the real network. Use the command:
npx hardhat run scripts/deploy.js --network reef
All of Hardhat projects use prettier and tslint.
You can check if your code style is correct by running yarn lint
, and fix
it with yarn lint:fix
.
To start working on your project, run
yarn build
then include the plugin in your hardhat
repo with yarn link
.
Versioning is done with the GH action: https://github.com/mikeal/merge-release.
Based on the commit messages, the version will be incremented from the latest release:
- If the string "BREAKING CHANGE" is found anywhere in any of the commit messages or descriptions the major version will be incremented.
- If a commit message begins with the string "feat" then the minor version will be increased. This works for most common commit metadata for feature additions: "feat: new API" and "feature: new API".
- All other changes will increment the patch version.