8 min read

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:

  1. Create and enter a directory on your machine where you see best fit.
mkdir Hello-World && cd Hello-World
  1. Create an npm project.
npm init -y
  1. Install Hardhat by running the following command in your terminal:
npm install --save-dev hardhat
  1. Initialize the Hardhat project by running the following command in your terminal:
npx hardhat init
  1. 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
  1. Create a new file called HelloWorld.sol in the contracts directory. This file will contain the smart contract code.
touch contracts/HelloWorld.sol
  1. 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

  1. First, lets add the testing library
npm install --save-dev @nomiclabs/hardhat-ethers 'ethers@^5.0.0'
  1. 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

  1. Now, let's create a create file called HelloWorld.js in the test directory. This file will contain the tests for our smart contract.
touch test/HelloWorld.test.js
  1. 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 the it function.
  • beforeEach test case, we deploy a new instance of the HelloWorld contract with await HelloWorld.deploy() and save it to the helloWorld variable.
  • In the first test case, we check that the initial message is "Hello, World!" by calling the getMessage function with await helloWorld.getMessage() and checking its return value with expect(...).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 calling getMessage again with await 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:

  1. Open a terminal window in the project directory.
  2. Run the following command to compile the smart contract:
npx hardhat compile
  1. 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 the HelloWorld contract, and ethers.getSigners() to get an array of signers (in this case, just the default signer). We save the contract factory to a variable called HelloWorld and the owner's signer to a variable called owner.
  // 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's deploy() function. We save the deployed contract instance to a variable called helloWorld.
  // 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 its setMessage() function. We save the new message to a variable called newMessage, log it to the console, and then use connect() 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 its getMessage() function. We save the returned message to a variable called message 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 calling main(). 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.

  1. 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
  1. 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
  1. Now, lets run our deploy.js script and deploy to to our localhost network, ran by hardhat node:
npx hardhat run --network localhost scripts/deploy.js
  1. 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.