Write a Simple Smart Contract in Truffle, Hardhat and Brownie
Let’s see how you can compile and test a simple solidity smart contract in different frameworks.
In this article we will be looking at three of the most famous smart contract development frameworks. We are going to take a simple smart contract “Greeter.sol” and compile and test it on each framework.
Introduction
Truffle and Hardhat both use JavaScript. Truffle has been around for a while but now there is a new kid on the block that is Hardhat which also uses JavaScript but with some additional features and simpler setup. The third candidate is Brownie which uses Python.
Hardhat
Hardhat has great documentation. I’m going to follow their documentation as it is and will then replicate all of that functionality in Truffle and Brownie. Let’s start.
Note: You need to have Node installed.
Install hardhat by running this command in your terminal:
npm install — save-dev hardhat
Now we’ll create a sample project:
npx hardhat
This will prompt you to select from the following options:
? What do you want to do? …
❯ Create a sample project
Create an empty hardhat.config.js
Quit
Select “Create a sample project”. It will ask you to install a few packages. If that doesn’t work you can also install them buy typing the following in the terminal:
npm install — save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers
This will create a bunch of files and folders in your current directory. The contracts folder will house the smart contracts. You’ll see Greeter.sol in this folder. The scripts folder keeps the code that deploys our contracts and the test folder holds the test scripts.
Here’s the Greeter.sol file:
//SPDX-License-Identifier: Unlicensepragma solidity ^0.8.0;import "hardhat/console.sol";contract Greeter { string greeting; constructor(string memory _greeting) { console.log("Deploying a Greeter with greeting:",_greeting); greeting = _greeting; } function greet() public view returns (string memory) { return greeting; } function setGreeting(string memory _greeting) public { console.log("Changing greeting from '%s' to '%s'", greeting, _greeting); greeting = _greeting; }}
Notice there is JavaScript written in the Solidity file. That’s something you don’t get in Truffle.
To test this code, there’s a test file sample-test.js.
const { expect } = require("chai");describe("Greeter", function () { it("Should return the new greeting once it's changed", async function () { const Greeter = await ethers.getContractFactory("Greeter"); const greeter = await Greeter.deploy("Hello, world!"); await greeter.deployed(); expect(await greeter.greet()).to.equal("Hello, world!"); const setGreetingTx = await greeter.setGreeting("Hola, mundo!"); // wait until the transaction is mined await setGreetingTx.wait(); expect(await greeter.greet()).to.equal("Hola, mundo!"); });});
To deploy the contract we have a file in scripts folder sample-script.js.
const hre = require("hardhat");async function main() { const Greeter = await hre.ethers.getContractFactory("Greeter"); const greeter = await Greeter.deploy("Hello, Hardhat!"); await greeter.deployed(); console.log("Greeter deployed to:", greeter.address);}main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1);});
Let’s compile the contract:
npx hardhat compile
This will compile the smart contracts in the contracts folder.
Deploy the contract:
npx hardhat run scripts/sample-script.js
Let’s now test the contract:
npx hardhat test
You’ll get something like this in the terminal:
Greeter
Deploying a Greeter with greeting: Hello, world!
Changing greeting from 'Hello, world!' to 'Hola, mundo!'
✓ Should return the new greeting once it's changed (868ms)1 passing (873ms)
This means that the test passed.
You can find the complete code here.
Now let’s do something similar in Truffle.
Truffle
Let’s install truffle.
Note: You need to have Node installed for truffle too.
npm install -g truffle
Now to initialize a boiler plate we run:
truffle init
It will create some files and folders similar to Hardhat. Put the Greeter.sol file in the contracts folder and the sample-test.js file in test folder (We’ll need to make some changes to the test file before running the test). Instead of scrips, however, we get a migrations folder. In that folder there is a file 1_initial_migration.js.
Replace this file’s content with the following:
const Greeter = artifacts.require("Greeter");module.exports = function (deployer) { deployer.deploy(Greeter, "Hello, World!");};
We will also need to tell Truffle that we are using solidity version 0.8.0. We can do that in the “truffle-config.js” file.
All you need to do is to un-comment the version of solc under compilers and set it to “0.8.0”.
...
compilers: { solc: { version: "0.8.0", // Fetch exact version from solc-bin (default: truffle's version)....
The test files need some changes to run on truffle. Make the following changes to the sample-test.js file:
const { expect } = require("chai");const Greeter = artifacts.require("Greeter");describe("Greeter", function () { it("Should return the new greeting once it's changed", async function () { const greeter = await Greeter.new("Hello, world!"); expect(await greeter.greet()).to.equal("Hello, world!"); await greeter.setGreeting("Hola, mundo!"); expect(await greeter.greet()).to.equal("Hola, mundo!"); });});
We can compile using the following command:
truffle compile
Now type:
Truffle develop
This will let you interact with the truffle CLI. (you can exit by typing .exit)
Type migrate to make migrations
And to run test, simply type truffle test and you should see the same test passing output.
You can find the complete code here.
Brownie
Let’s try to run the same contract using Brownie. You might think that since this framework uses a different programming language, it will be a completely different environment but trust me all of the functionality and the project structure is quite similar.
So let’s get started.
Note: To install Brownie you need to have pip installed.
Type the following command:
pip install eth-brownie
One you have Brownie installed you can create a project by typing:
brownie init
You’ll see that the project structure is almost the same as that of Truffle and Hardhat. Again the folders we’ll be looking at the most are contracts, scripts, and tests. They have the same purpose i.e. contracts and tests contain smart contracts written in Solidity and tests written in Python respectively. The scripts folder stores the deployment script.
Let’s now put the Greeter.sol file in the contracts folder and run:
brownie compile
It will compile the smart contract.
Now create a file deploy.py in scripts folder and paste the following code in it:
from brownie import accounts, Greeter
def main(): deployer_account = accounts[0] greeter = Greeter.deploy("Hello, World!", {'from': deployer_account}) print("Deployed at: ", greeter.address)
It will deploy the contract once you run:
brownie run scripts/deploy.py
Now Let’s write a simple test in Python. Create a new file greeter_test.py in tests folder and put the following code in it:
import pytestfrom brownie import Greeter, accounts
@pytest.fixturedef greeter(): return accounts[0].deploy(Greeter, "Hello, World!") # Check if the contract was deployed with the correct greeting def test_greeter(greeter): assert greeter.greet() == "Hello, World!" # Check if the greeting changes def test_set_greeting(greeter): greeter.setGreeting("Hola, mundo!") assert greeter.greet() == "Hola, mundo!"
To run the test type the following in the terminal:
brownie test
You’ll see 2 tests pass.
You can find the complete code here.
This brief introduction is to let you have a quick glance at three major smart contract development frameworks, and hopefully to help you decide which one to choose for your project.