Your first smart contract

In this tutorial, you'll learn to create your first smart contract with Hardhat – a full-featured development environment for contract compilation, deployment and verification.

In this tutorial we'll guide you:

  • Setting up your Node.js environment for U2U smart contract development

  • Creating and configuring a Hardhat project

  • The basics of a Solidity smart contract that implements a time-locked wallet

  • Writing automated tests for your contract using Hardhat

  • ...

You should get familiar to:

  • Write code in Javascript

  • Operate a terminal

  • Use git

  • Understand the basics of how smart contracts work

Initialization

First, we’ll need to create a folder for our project. Navigate to your command line and type:

mkdir first-contract
cd first-contract

Create an empty Node.js project:

npm init -y

Add Hardhat as a dependency to the project:

npm install --save-dev hardhat

Then use Hardhat to initialize your project:

npx hardhat init

Then it will appear as follow, please select Create a Typescript project:

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

👷 Welcome to Hardhat v2.17.4 👷‍

? What do you want to do? … 
  Create a JavaScript project
❯ Create a TypeScript project
  Create an empty hardhat.config.js
  Quit

It will ask more questions, just follow them and finish setting up your sample project, below are example:

✔ What do you want to do? · Create a TypeScript project
✔ Hardhat project root: · /Users/steven/codekeeper/u2u/sample_project
✔ Do you want to add a .gitignore? (Y/n) · y
✔ Do you want to install this sample project's dependencies with npm (@nomicfoundation/hardhat-toolbox)? (Y/n) · y

Hardhat has scaffolded a project contains Lock contract. Our sample project should contain these folders and files:

contracts/
scripts/
test/
hardhat.config.ts

These are the default paths for a Hardhat project.

  • contracts/ is where the source files for your contracts should be.

  • test/ is where your tests should go.

  • scripts/ is where simple automation scripts go.

Write your first smart contract

Open up contracts folder, you'll see sample contract file Lock.sol that Hardhat has initialized:

contracts/
    Lock.sol

Now let's take a glance at Lock.sol:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

// Uncomment this line to use console.log
// import "hardhat/console.sol";

contract Lock {
    uint public unlockTime;
    address payable public owner;

    event Withdrawal(uint amount, uint when);

    constructor(uint _unlockTime) payable {
        require(
            block.timestamp < _unlockTime,
            "Unlock time should be in the future"
        );

        unlockTime = _unlockTime;
        owner = payable(msg.sender);
    }

    function withdraw() public {
        // Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
        // console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);

        require(block.timestamp >= unlockTime, "You can't withdraw yet");
        require(msg.sender == owner, "You aren't the owner");

        emit Withdrawal(address(this).balance, block.timestamp);

        owner.transfer(address(this).balance);
    }
}

This Solidity smart contract is called "Lock" and is designed to hold and manage funds until a specified unlock time has passed. Let's break down the key components and functionality of this contract:

  1. State Variables:

    • uint public unlockTime: This variable stores the timestamp when the contract will allow funds to be withdrawn. It's publicly accessible, so anyone can check when the funds will become available.

    • address payable public owner: This variable holds the Ethereum address of the owner of the contract, who has the authority to withdraw funds.

  2. Events:

    • event Withdrawal(uint amount, uint when): This event is emitted when a withdrawal is made. It logs the amount withdrawn and the timestamp at which the withdrawal occurred.

  3. Constructor:

    • constructor(uint _unlockTime) payable: This constructor is executed when the contract is deployed. It takes a single argument, _unlockTime, which represents the timestamp in the future when the funds can be withdrawn. The require statement ensures that the unlock time is indeed in the future. The msg.sender is set as the owner of the contract, and the constructor can receive an initial amount of Ether.

  4. Function - withdraw:

    • function withdraw() public: This function allows the owner to withdraw the funds held in the contract. It enforces the following conditions:

      • The current block timestamp (block.timestamp) must be greater than or equal to the unlockTime, ensuring that funds can only be withdrawn after the specified unlock time.

      • The sender (i.e., msg.sender) must be the owner of the contract.

    If these conditions are met, the function emits the Withdrawal event to log the withdrawal details, and it transfers the entire balance of the contract to the owner's address using owner.transfer(address(this).balance).

Additionally, there is a commented-out line (console.log) that can be used for debugging purposes when working with a development environment like Hardhat. It's used to print information to the console but is typically commented out in production code.

What's next?

Finally you have your own smart contract. The next step is to test it carefully before deploy it to blockchain. Please refer to next section at Test your smart contract.

Last updated