Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Initializing Contract State with Constructors

Hello! Welcome back to our journey into Solidity and smart contract development.

In the last lesson, we focused on what functions are allowed to do with a contract's state during its lifetime, using the view and pure keywords. Now, we'll shift our attention to the very beginning of a contract's life: its creation.

Today's lesson addresses a fundamental part of a contract's lifecycle: how to implement a constructor to initialize contract state upon deployment. Given your background in backend development with languages like Java, the concept of a constructor should feel familiar. It's a special function that sets up an object's initial state—and in Solidity, it does the same for a smart contract.

What is a Constructor?

A constructor is an optional, special function that is executed only once, at the exact moment a smart contract is deployed to the blockchain. Its primary purpose is to set the initial values of your state variables.

Key properties of a constructor:

  • It has the special name constructor.
  • A contract can only have one constructor.
  • It is executed only once and cannot be called again after deployment.
  • It's the perfect place to set up permanent values, like the contract owner, the name of a token, or its initial supply.

Constructor | Solidity 0.8

To start, let's watch a quick, clear explanation of what a constructor is and how to define one. This video from Smart Contract Programmer covers the core concepts perfectly.

Watch the video from the beginning until 01:23. Pay close attention to the syntax, how it uses msg.sender to set an owner, and how it accepts an input parameter to initialize another state variable.

Initializing State: Simple vs. Parameterized

Constructors can be simple (taking no arguments) or parameterized (taking arguments).

1. Simple Constructor: Setting the Owner

A common and critical use case for a constructor is to permanently set the contract's owner. Inside the constructor, the global variable msg.sender refers to the address of the account that is deploying the contract.

Let's look at the official Hardhat tutorial's Token.sol example.

Writing and compiling smart contracts

This official Hardhat documentation provides a great example of a simple constructor in the context of a token contract.

In the code block for Token.sol, locate the constructor() function. Notice how it takes no arguments but uses msg.sender to initialize the owner state variable and assign the totalSupply to the deployer's balance.

Here is the relevant snippet from that contract:

// An address type variable is used to store ethereum accounts.
address public owner;

// A mapping is a key/value map. Here we store each account's balance.
mapping(address => uint256) balances;

uint256 public totalSupply = 1000000;

/**
 * Contract initialization.
 */
constructor() {
    // The totalSupply is assigned to the transaction sender, which is the
    // account that is deploying the contract.
    balances[msg.sender] = totalSupply;
    owner = msg.sender;
}

In this case, whoever deploys this contract automatically becomes the owner and receives all the initial tokens.

2. Parameterized Constructor: Customizing at Deployment

The real power of constructors comes from their ability to accept arguments. This allows you to deploy multiple, uniquely configured instances of the same contract without changing the source code. For your goal of creating a token, this is how you'll set its name, symbol, and total supply at the moment of creation.

Solidity Constructor and Deployment in Remix IDE
This image shows the connection between a constructor that accepts an argument (the `banker` address) in the code and the deployment interface where you would provide that argument.

This is where the process connects to your development environment. When you deploy a contract using a tool like Hardhat, your deployment script will pass the required arguments to the constructor.

Let's see how this works in a more realistic token example.

Deploying ERC20 Token With Hardhat: A Step-by-Step Guide

This guide from Semaphore demonstrates deploying an ERC20 token and shows a constructor that takes arguments. It then shows the corresponding deployment script that passes those arguments.

First, read the section 'Creating the ERC20 Token' and find the MyToken.sol code. Pay close attention to the constructor which accepts cap and reward. Then, skip down to the section 'Creating Our Deploy Script' and examine the main function in the deploy.js script. Notice the line MyToken.deploy(100000000, 50). This is the key part that passes the values to the constructor during deployment.

As you saw in the article, the contract's constructor is defined to accept a cap and a reward:

// From MyToken.sol
constructor(uint256 cap, uint256 reward) 
    ERC20("MyToken", "MYT") 
    ERC20Capped(cap * (10 ** decimals())) {
        owner = payable(msg.sender);
        _mint(owner, 50000000 * (10 ** decimals()));
        blockReward = reward * (10 ** decimals());
}

(Note: ERC20(...) and ERC20Capped(...) are calls to the constructors of parent contracts, a concept we'll cover later.)

And the deployment script passes these values in the deploy function call:

// From deploy.js
async function main() {
  const MyToken = await hre.ethers.getContractFactory("MyToken");
  
  // The arguments 100000000 and 50 are passed to the constructor here.
  const myToken = await MyToken.deploy(100000000, 50);

  await myToken.deployed();
  console.log("MyToken deployed to: ", myToken.address);
}

This elegant mechanism allows you to configure your contract dynamically at the moment of creation.

Test your understanding!
  1. Can a smart contract have more than one constructor?
  2. You deploy a contract with constructor() { owner = msg.sender; }. A week later, you call a public function on that contract. What is the value of msg.sender inside that public function?
Show answer
  1. No. A contract can have at most one constructor. If no constructor is defined, a default empty constructor constructor() public {} is used.
  2. The value of msg.sender will be your address (the caller of the function). msg.sender always refers to the immediate caller of the current function. It was the deployer's address during construction, but in subsequent calls, it reflects who is calling now. The owner state variable, however, remains set to the deployer's address.

Let's Practice

Now it's your turn to write a constructor.

  1. In your Hardhat project, create a new file in the contracts directory called Configurable.sol.
  2. Inside this file, define a contract named Configurable.
  3. Add the following state variables to the contract:
    • string public message;
    • uint256 public initialValue;
    • address public creator;
  4. Your task is to write a constructor that:
    • Accepts a string argument _message and a uint256 argument _initialValue.
    • Sets the message state variable to the value of _message.
    • Sets the initialValue state variable to the value of _initialValue.
    • Sets the creator state variable to msg.sender.

Once you've written the code, run npx hardhat compile in your terminal to ensure there are no syntax errors.

Conclusion

You've now mastered one of the most important functions in a smart contract's life. The constructor provides a secure and efficient way to initialize your contract's state, setting the foundation for all its future operations.

Key Takeaways:

  • The constructor is a special function that runs only once when a contract is deployed.
  • It's the ideal place to set initial values for state variables like the owner, token name, or total supply.
  • msg.sender inside the constructor always refers to the address of the account deploying the contract.
  • Arguments can be passed to the constructor from a deployment script (e.g., MyContract.deploy(arg1, arg2)), allowing for dynamic configuration.

Preview of the Next Lesson:

You've seen how a deployment script passes arguments to a constructor, but we've only scratched the surface of what those scripts do. In our next lesson, we will fully focus on how to write and execute a simple deployment script using Hardhat. You'll learn the standard structure of a deployment script and run one yourself to deploy a contract to a local test network.

Can't find a good explanation? Sign up and we'll make it for you

Sign up