App - Hello World
Introduction
Solidity is a contract-oriented, high-level language used for implementing smart contracts on the Ethereum blockchain. A smart contract is a self-executing contract that facilitates, verifies, or enforces the negotiation or performance of a contract.
In this blog post, we will explain a "Hello World" smart contract in Solidity 0.8.16. We will cover all the elements of the contract and explain their functions.
Prerequisites
To understand this blog post, you should have a basic understanding of Solidity and Ethereum. You should also have a development environment setup to write, compile, and deploy smart contracts.
The Hello World Smart Contract
Let's start with the code for the Hello World smart contract:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.16;
contract SimpleStorage {
string public message;
constructor() {
message = "Hello, World!";
}
function getMessage() public view returns (string memory) {
return message;
}
}
Let's break down the code line by line.
Line 1:
// SPDX-License-Identifier: MIT
This is a comment line that specifies the license for the contract. This line is optional, but it is good practice to specify a license.
pragma solidity 0.8.16;
This line specifies the version of Solidity that the contract uses via the Pragma. In this case, we are using version 0.8.16.
Line 4-8:
contract HelloWorld {
string public message;
constructor() {
message = "Hello, World!";
}
These lines define the HelloWorld
contract. The Strings data type is used to store the message. The public
modifier allows other contracts to access the message
variable. The Constructor function is executed when the contract is deployed, and it initializes the message
variable with the "Hello, World!" string.
Line 10-14:
function getMessage() public view returns (string memory) {
return message;
}
This Function allows other contracts to read the value of the message
variable. The public
modifier makes the function accessible to other contracts, and the view
modifier specifies that the function does not modify the state of the contract. The returns
keyword specifies the return type of the function, which is a string
in this case. The function returns the value of the message
variable.
Line 16-20:
function setMessage(string memory newMessage) public {
message = newMessage;
}
This function allows other contracts to update the value of the message
variable. The public
modifier makes the function accessible to other contracts, and there is no view
modifier because the function modifies the state of the contract. The string
parameter newMessage
is used to update the value of the message
variable.
Setting up the Project with Hardhat
Now that we have a high level overview of the code, let's add our "Hello World" smart contract to a local project. Here are the steps to set up the project using Hardhat:
- Create and enter a directory on your machine where you see best fit.
mkdir Hello-World && cd Hello-World
- Create an
npm
project.
npm init -y
- Install Hardhat by running the following command in your terminal:
npm install --save-dev hardhat
- Initialize the Hardhat project by running the following command in your terminal:
npx hardhat init
- Choose create a JavaScript project and type 'y' for each selection. Then delete the example files:
rm contracts/Lock.sol test/Lock.js scripts/deploy.js
- Create a new file called
HelloWorld.sol
in thecontracts
directory. This file will contain the smart contract code.
touch contracts/HelloWorld.sol
- Let's add "Hello World" smart contract to
contracts/HelloWorld.sol
:
// SPDX-License-Identifier: MIT
// HelloWorld.sol
pragma solidity ^0.8.16;
contract HelloWorld {
string public message;
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
Testing
Testing is an essential part of any software development process, and smart contracts are no exception. . In this section, we will describe how to test the "Hello World" smart contract written in JavaScript using Hardhat.
To deploy and test the Hello World smart contract, you will need a development environment for building, testing, and deploying smart contracts on the Ethereum blockchain. Popular options include Truffle, Hardhat, or Foundry. Remix is another browser based development environment. However, it's limited to only Solidity code and not the front end.
For our purposes we will walk through doing this with HardHat and using Visual Studio Code.
Let's look at testing. After that, we will work on the front end.
Configuration
- First, lets add the testing library
npm install --save-dev @nomiclabs/hardhat-ethers 'ethers@^5.0.0'
- Now lets configure out
hardhat.config.js
file:
require("@nomicfoundation/hardhat-toolbox");
require("@nomiclabs/hardhat-ethers");
/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.16",
};
Writing the Tests
- Now, let's create a create file called
HelloWorld.js
in thetest
directory. This file will contain the tests for our smart contract.
touch test/HelloWorld.test.js
- Now let's write the tests for our smart contract. Here's an example of how to write tests for the
HelloWorld
contract using Hardhat:
// test/HelloWorld.test.js
// Importing the `expect` assertion library from the Chai.js package
const { expect } = require("chai");
// Defining a new Mocha test suite with the `describe` function.
// The first argument is a string describing the test suite, and the second argument is a callback function that defines the tests.
describe("HelloWorld contract", function () {
// Declaring two variables to store the contract factory and the contract instance.
let HelloWorld;
let helloWorld;
// Defining a Mocha `beforeEach` hook, which runs before each test case in the suite.
// This hook deploys a new instance of the `HelloWorld` contract and saves it to the `helloWorld` variable.
beforeEach(async function () {
HelloWorld = await ethers.getContractFactory("HelloWorld");
helloWorld = await HelloWorld.deploy();
await helloWorld.deployed();
});
// Defining a Mocha test case with the `it` function.
// The first argument is a string describing the test case, and the second argument is a callback function that defines the test logic.
it("should return the initial message", async function () {
// Using the `expect` assertion to check that calling the `getMessage` function on the contract instance returns the expected value ("Hello, World!").
expect(await helloWorld.getMessage()).to.equal("");
});
// Defining another Mocha test case with the `it` function.
it("should allow the message to be set", async function () {
// Declaring a new string variable to hold the message we want to set on the contract.
const newMessage = "Hello, Hardhat!";
// Using the `await` keyword to call the `setMessage` function on the contract instance and set a new message.
await helloWorld.setMessage(newMessage);
// Using the `expect` assertion to check that calling the `getMessage` function on the contract instance returns the expected value (the new message we just set).
expect(await helloWorld.getMessage()).to.equal(newMessage);
});
});
To explain what's happening in the code above:
- We're importing the
expect
assertion library from Chai.js. - We're using Mocha to define a test suite with the
describe
function. Inside this suite, we define two test cases with theit
function. beforeEach
test case, we deploy a new instance of theHelloWorld
contract withawait HelloWorld.deploy()
and save it to thehelloWorld
variable.- In the first test case, we check that the initial message is "Hello, World!" by calling the
getMessage
function withawait helloWorld.getMessage()
and checking its return value withexpect(...).to.equal(...)
. - In the second test case, we set a new message with
await helloWorld.setMessage(newMessage)
and then check that the message has been updated by callinggetMessage
again withawait helloWorld.getMessage()
and checking its return value.
Running the Tests
Now that we have written our tests, we can run them using Hardhat. Here's how to run the tests:
- Open a terminal window in the project directory.
- Run the following command to compile the smart contract:
npx hardhat compile
- Run the following command to run the tests:
npx hardhat test
If everything is working correctly, you should see a message indicating that the tests passed.
Deploying your DApp to Local Network
Now let's play with our Hello, World app on our local network. To do so, we will run an instance of hardhat node
, a local Ethereum network that lets us deploy, test, and interact with our smart contracts.
Next, let's examine our deploy script:
// deploy.js
async function main() {
// Hardhat Ethereum library import
const { ethers } = require("hardhat");
// Getting the ContractFactory and the signer (default account)
const HelloWorld = await ethers.getContractFactory("HelloWorld");
const [owner] = await ethers.getSigners();
// Deploying the contract
console.log("Deploying HelloWorld contract...");
const helloWorld = await HelloWorld.deploy();
// Waiting for the contract to be deployed
await helloWorld.deployed();
// Printing the deployed contract's address
console.log("HelloWorld contract deployed to:", helloWorld.address);
// Setting a message on the contract
const newMessage = "Hello, World!";
console.log(`Setting new message: ${newMessage}`);
await helloWorld.connect(owner).setMessage(newMessage);
// Retrieving the message from the contract
const message = await helloWorld.getMessage();
console.log(`Retrieved message: ${message}`);
}
// Running the deployment script
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
Let's deep dive into what's happening in the code above:
async function main() {
- This line imports the
ethers
library from Hardhat. We'll use this library to interact with the Ethereum network.
// Hardhat Ethereum library import
const { ethers } = require("hardhat");
- This line defines an asynchronous function called
main()
. We'll use this function to define the logic for deploying the contract.
// Getting the ContractFactory and the signer (default account)
const HelloWorld = await ethers.getContractFactory("HelloWorld");
const [owner] = await ethers.getSigners();
- These lines use
ethers.getContractFactory()
to get a contract factory for theHelloWorld
contract, andethers.getSigners()
to get an array of signers (in this case, just the default signer). We save the contract factory to a variable calledHelloWorld
and the owner's signer to a variable calledowner
.
// Deploying the contract
console.log("Deploying HelloWorld contract...");
const helloWorld = await HelloWorld.deploy();
- This line deploys the
HelloWorld
contract to the local Hardhat network using the contract factory'sdeploy()
function. We save the deployed contract instance to a variable calledhelloWorld
.
// Waiting for the contract to be deployed
await helloWorld.deployed();
- This line waits for the contract to be deployed to the network before continuing. This ensures that the contract is fully deployed and available for use.
// Printing the deployed contract's address
console.log("HelloWorld contract deployed to:", helloWorld.address);
- This line logs the deployed contract's address to the console. This will allow us to interact with the contract using its address.
// Setting a message on the contract
const newMessage = "Hello, Hardhat!";
console.log(`Setting new message: ${newMessage}`);
await helloWorld.connect(owner).setMessage(newMessage);
- These lines set a new message on the
HelloWorld
contract by calling itssetMessage()
function. We save the new message to a variable callednewMessage
, log it to the console, and then useconnect()
to specify the owner's signer as the message sender.
// Retrieving the message from the contract
const message = await helloWorld.getMessage();
console.log(`Retrieved message: ${message}`);
- These lines retrieve the message from the
HelloWorld
contract by calling itsgetMessage()
function. We save the returned message to a variable calledmessage
and log it to the console.
// Running the deployment script
main()
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
- These lines close the
main()
function and run it by callingmain()
. If the function completes successfully, the script exits with a status code of 0. If there's an error, it's logged to the console and the script exits with a status code of 1.
Deploying to local network
Note: that this code assumes you have installed Hardhat and it's dependencies, and that you have compiled the HelloWorld
contract with npx hardhat compile
. You can run this script with the command npx hardhat run deploy.js
.
- In order to deploy to any network, we must first create a
deploy.js
script in our/script
directory. Then add the code above.
touch scripts/deploy.js
- Open a new terminal tab to start
hardhat node
:
Note: Similar to running a local web server, keep this running to be able to interact with the contract. If the instance is closed, the local network will not remember your contract or its state, and you will have redeploy again.
npx hardhat node
- Now, lets run our
deploy.js
script and deploy to to ourlocalhost
network, ran byhardhat node
:
npx hardhat run --network localhost scripts/deploy.js
- You should see the following output meaning that you contract deployment was successful:
Note: the address (0x5FbDB...aa3
) which the contract was deployed to may vary on your machine.
Deploying HelloWorld contract...
HelloWorld contract deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3
Setting new message: Hello, World!
Retrieved message: Hello, World!
Success! You have created, tested and deployed your first smart contract!
Hello World!
Conclusion
In this blog post, we explained how to set up the project, write the smart contract, and write the tests using Hardhat's testing framework. Additionally, we explained a Hello World smart contract in Solidity 0.8.16
. We covered all the elements of the contract and explained their functions.
With this knowledge, you can begin your web3 journey to building smart contracts and decentralized applications.
Feedback
Have feedback? Found something that needs to be updated, accurate, or explained?
Join our discord and share what can be improved.
Any and all feedback is welcomed.