How to deploy your first smart contract on Ethereum with Solidity and Hardhat
Tags: ethereum, solidity, web3, hardhat
Published: 2021-09-18 06:37:57


I was planning to release a tutorial on how to deploy your first NFT smart contract but while I was writing it I understood that more than half of the content was about how to set up **Hardhat** to have the **perfect development environment** ready to go for your next project.

So let’s start from here and when I’ll release the NFT tutorial you can leverage the work we have done today to be ready instantly to start developing your next smart contract. Shall we proceed?

## What are you going to learn today?

-   Configure Hardhat to compile, deploy, test, and debug your Solidity code
-   Write a smart contract
-   Test your smart contract
-   Deploy your smart contract on Rinkeby
-   Get your smart contract verified by Etherscan

## Hardhat

[Hardhat](https://hardhat.org/) is the core tool we will be using today and a must-have if you need to develop and debug your smart contract locally before deploying it on the test net and then on the main net.

### What is Hardhat?

Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software. It helps developers manage and automate the recurring tasks that are inherent to the process of building smart contracts and dApps, as well as easily introducing more functionality around this workflow. This means compiling, running, and testing smart contracts at the very core.

### Why use hardhat?

-   You can run Solidity locally to **deploy, run, test, and debug** your contracts without dealing with live environments.
-   You get Solidity stack traces, console.log, and explicit error messages when transactions fail. Yeah, you heard it right, **console.log** in your Solidity contract!
-   Tons of **plugins** to add cool functionalities and allows you to integrate your existing tools. We will use a couple of them to feel that magic!
-   Full native support for **TypeScript**.

### Useful links to learn more

-   [Official website](https://hardhat.org/)
-   [Plugins](https://hardhat.org/plugins/)
-   [Documentation](https://hardhat.org/getting-started/)
-   [Tutorial](https://hardhat.org/tutorial/)
-   [GitHub Repository](https://github.com/nomiclabs/hardhat)
-   [Discord Community](https://hardhat.org/discord)

## Create your first Hardhat project

It’s time to open your Terminal, your Visual Studio code IDE, and start building!

### What project are we going to build?

In this tutorial, we are going to build an “easy” smart contract. It will be pretty stupid but our main goal here is to configure hardhat and deploy the contract, not for the contract purpose.

What will the contract do?

-   Track the “purpose” of the contract in a string that will be publicly visible
-   You can change the purpose only if you pay more than the previous owner
-   You cannot change the purpose if you are already the owner
-   You can withdraw your ETH only if you are not the current purpose’s owner
-   Emit a PurposeChange event when the purpose has changed

### Create the project, init it and configure Hardhat

Open your terminal and let’s go. We will call our project **world-purpose!**

```
mkdir world-purpose  
cd world-purpose  
npm init -y  
yarn add --dev hardhat  
npx hardhat
```

With `npx hardhat` the hardhat wizard will kickstart helping you configure your project.

-   Choose `Create a basic sample project`
-   Choose the default `Hardhat project root` because in this tutorial we are only configuring hardhat.
-   Confirm to add the `.gitignore`
-   Confirm to install the `sample project's dependecies with yarn` this will install some required dependencies for your project that we will use along the way

Now your project structure should have these files/folder

-   `contracts` where all your contracts will be stored
-   `scripts` where all your scripts/tasks will be stored
-   `test` where your smart contract tests will be stored
-   `hardhat.config.js` where you will configure your hardhat project

### Important hardhat commands and concepts you should master

-   [Hardhat Configuration file](https://hardhat.org/config/)
-   [Hardhat Tasks](https://hardhat.org/guides/create-task.html)
-   `npx hardhat node` — Run the node task to start a JSON-RPC server on top of Hardhat Network (your local ethereum blockchain)
-   `npx hardhat test` — To run tests stored in the test folder
-   `npx hardhat compile` — To compile the entire project, building your smart contracts
-   `npx hardhat clean` — To clean the cache and delete compiled smart contracts
-   `npx hardhat run —-network <network> script/path` — To run a specific script in a specific `network`

### Add TypeScript Support

As we said Hardhat supports typescript natively and we will be using it. Using javascript or typescript doesn’t change much but this is my personal preference because I think that it allows you to make fewer errors and understand better how to use external libraries.

Let’s install some needed dependencies

`yarn add --dev ts-node typescriptyarn add --dev chai @types/node @types/mocha @types/chai`ddd

Change the hardhat configuration file extension to `.ts` and update the content with this

{% gist https://gist.github.com/StErMi/37563943d112bafd48ed6773fbeeae5b %}

Now create a default `tsconfig.json` in the root of your project with this content

```json
{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "dist"
  },
  "include": [
    "./scripts",
    "./test"
  ],
  "files": [
    "./hardhat.config.ts"
  ]
}
```

Now update also the test file and script file to the typescript file type `.ts` and change their code.

{% gist https://gist.github.com/StErMi/8d4c97be82600d468ade7d6bbf841754 %}

{% gist https://gist.github.com/StErMi/47a9a6bbead62fa70c0315a911c03a36 %}

Let’s test that everything is working correctly

```
npx hardhat test  
npx hardhat node  
npx hardhat run --network localhost scripts/sample-script.ts
```

## Let’s add some Hardhat plugins and some code utilities

### add solhint, solhint-plugin-prettier, and hardhat-solhint plugin

Now we want to add support to `solhint` a linting utility for Solidity code that will help us to follow strict rules while we develop our smart contract. These rules a useful for both follow the code style standard best practice and adhering to the best security approaches.

**solhint+solhint prettier**

`yarn add --dev solhint solhint-plugin-prettier prettier prettier-plugin-solidity`

Add a `.solhint.json` configuration file in your root folder

```json
{  
  "plugins": ["prettier"],  
  "rules": {  
    "prettier/prettier": "error"  
  }  
}
```

Add a `.prettierrc` config file and add styles as you prefer. This is my personal choice

```json
{  
  "arrowParens": "always",  
  "singleQuote": true,  
  "bracketSpacing": false,  
  "trailingComma": "all",  
  "printWidth": 120,  
  "overrides": [  
    {  
      "files": "*.sol",  
      "options": {  
        "printWidth": 120,  
        "tabWidth": 4,  
        "useTabs": false,  
        "singleQuote": false,  
        "bracketSpacing": false,  
        "explicitTypes": "always"  
      }  
    }  
  ]  
}
```

If you want to know more about solhint and its prettier plugin checkout their documentation site

-   [Solhint](https://github.com/protofire/solhint/)
-   [Prettier Plugin Solidity](https://github.com/prettier-solidity/prettier-plugin-solidity)
-   [Solhint plugin Prettier](https://github.com/fvictorio/solhint-plugin-prettier)

**hardhat-solhint**

If you want to know more follow the Hardhat [solhint plugin documentation](https://hardhat.org/plugins/nomiclabs-hardhat-solhint.html).

`yarn add --dev @nomiclabs/hardhat-solhint`

and update the hardhat config adding this line to the import

`import “[@nomiclabs/hardhat-solhint](http://twitter.com/nomiclabs/hardhat-solhint "Twitter profile for @nomiclabs/hardhat-solhint")”;`

### typechain for typed contract support

This part is totally optional. As I said I love typescript and I like to use it everywhere whenever it’s possible. Adding support to contract types allow me to know perfectly which functions are available, which parameters are needed of which type, and what are they returning.

You could totally go without it but I highly suggest you follow this step.

Why typechain?

-   **Zero Config Usage** — Run the _compile_ task as normal, and Typechain artifacts will automatically be generated in a root directory called `typechain`.
-   **Incremental generatio**n — Only recompiled files will have their types regenerated
-   **Frictionless** — return type of `ethers.getContractFactory` will be typed properly - no need for casts

If you want to dig deeper into these projects and know all the possible configuration options you can follow these links:

-   [Typechain Hardhat](https://hardhat.org/plugins/typechain-hardhat.html)
-   [Typechain GitHub Repository](https://github.com/dethcrypto/TypeChain)

Let’s do it

`yarn add --dev typechain @typechain/hardhat @typechain/ethers-v5`

Add these imports to your Hardhat config file

`import '@typechain/hardhat'import '@nomiclabs/hardhat-ethers'import '@nomiclabs/hardhat-waffle'`

Add `"resolveJsonModule": true` to your `tsconfig.json`

Now when you compile your contract typechain will generate the corresponding types into the `typechain` folder and you will be able to use it in your tests and web app!

Updated the tests file to use Typechain generated types

{% gist https://gist.github.com/StErMi/0af4a7b9afd975e8d5c1a6ec4b6fee99 %}

### Update scripts in `package.json`

This part is totally optional but it allow you to run command faster and preconfigure them. Open your `package.json` and override the `scripts` section with these commands:

```json
"scripts": {
  "clean": "npx hardhat clean",
  "chain": "npx hardhat node",
  "deploy": "npx hardhat run --network localhost scripts/deploy.ts",
  "deploy:rinkeby": "npx hardhat run --network rinkeby scripts/deploy.ts",
  "test": "npx hardhat test"
}
```

Now if you want to simply run the Hardhat node you can write in your console `yarn chain` and boom! We are ready to go!

> If you have skipped the TypeScript part and you want to use only Javascript just change those `.ts` back to `.js` and everything will work as expected.

## Developing the smart contract

Ok, now we are really ready to go. Rename `Greeter.sol` in your `contracts` folder to `WorldPurpose.sol` and we will start building from here.

Our contract needs to do these things:

-   Track the “purpose” of the contract in a struct
-   You can change the purpose only if you pay more than the previous owner
-   You cannot change the purpose if you are already the owner
-   You can withdraw your ETH only if you are not the current purpose’s owner
-   Emit a PurposeChange event when the purpose has changed

### Concepts you should master

-   [Variable Types](https://docs.soliditylang.org/en/v0.8.7/types.html)
-   [Mapping](https://docs.soliditylang.org/en/v0.8.7/types.html#mapping-types)
-   [Structs](https://docs.soliditylang.org/en/v0.8.7/types.html#structs)
-   [Functions](https://docs.soliditylang.org/en/v0.8.7/structure-of-a-contract.html?highlight=function#functions)
-   [Events](https://docs.soliditylang.org/en/v0.8.7/structure-of-a-contract.html?highlight=function#functions)
-   [Payable functions](https://solidity-by-example.org/payable/)

### Smart Contract Code

Let’s start by creating a Struct to store the Purpose information

```
/// [@notice](http://twitter.com/notice "Twitter profile for @notice") Purpose struct  
struct Purpose {  
    address owner;  
    string purpose;  
    uint256 investment;  
}
```

Let’s now define some state variables to track both the current Purpose and user’s investment

```ts
/// @notice Track user investments
mapping(address => uint256) private balances;

/// @notice Track the current world purpose
Purpose private purpose;
```

Last but not least define an Event that will be emitted when a new World Purpose has been set

```ts
/// @notice Event to track new Purpose
event PurposeChange(address indexed owner, string purpose, uint256 investment);
```

Let’s create some utility functions to get the current `purpose` and the user’s balance that is still in the contract. We need to do that because `balances` and `purpose` variables are `private` so they cannot be accessed directly from external contract/web3 apps. We need to expose them via these functions

```ts
/**
 @notice Getter for the current purpose
 @return currentPurpose The current active World purpose
*/
function getCurrentPurpose() public view returns (Purpose memory currentPurpose) {
    return purpose;
}

/**
 @notice Get the total amount of investment you have made. It returns both the locked and unloacked investment.
 @return balance The balance you still have in the contract
*/
function getBalance() public view returns (uint256 balance) {
    return balances[msg.sender];
}
```

Now let’s create the `setPurpose` function. It takes one parameter as input: `_purpose` . The function must be `payable` because we want to accept some Ether in order to set the purpose (those ethers will be withdrawable by the owner the purpose has been overridden by someone else).

The transaction will `revert` if some conditions are not met:

-   `_purpose` input parameter is empty
-   the `msg.value` (amount of ether sent with the transaction) is empty (0 ethers)
-   the `msg.value` is less than the current purpose
-   the current world purpose owner tries to override his purpose (`msg.sender` must be different from the current owner)

{% gist https://gist.github.com/StErMi/69508b97d23430a898a75dd997e431a5 %}

If everything works we set the new purpose, update the balance and emit the event.

Now we want to allow users to withdraw their investment from previous purposes. Please note that only the current purpose’s investment is “locked”. It will be unlocked only when a new person will set the new purpose.

{% gist https://gist.github.com/StErMi/26ab0a185022af8aa21ea295b7794c8b %}

Deploy it locally just to test that everything works as expected. You should see something like this:

![Deploy on local hardhat network](https://cdn-images-1.medium.com/max/1600/1*fJosmmEHgBQ3Qlii0KsdwA.png)

Contract deployment on local blockchain successful

### Add local Test

Now I will not go into detail about the code inside the testing file but I’ll explain the concept behind it. You should always create test cases for your contract. It’s a fast way to understand if the contact’s logic is working as expected and allows you to prevent deploying something that it’s not working.

My current workflow is like this: I take every function and I test that they do what I expect them to do in both success cases and revert cases. Nothing more, nothing less.

When you write tests for Solidity contracts you are going to use [**Waffle**](https://getwaffle.io/). Head over to their website to have an overview of the library, it’s really well made and offers a lot of utilities.

Now, delete the file you have in your `test` folder, create a new one called `worldpurpose.ts` and replace the content with this

{% gist https://gist.github.com/StErMi/1543c1223908312ec9b987765b768239 %}

Execute your tests with `yarn test` and see if everything pass as expected

![Run tests locally](https://cdn-images-1.medium.com/max/1600/1*StnzBuyj7tM9QUxpmitwvg.png)

Tests execution

## Deploy your contract on test net

> **===== HUGE DISCLAIMER START =====**
> **DO NOT USE YOUR MAIN WALLET PRIVATE FOR TESTING PURPOSES**
> **USE A BURNER WALLET OR CREATE A NEW WALLET**
> **===== HUGE DISCLAIMER END =====**

We have already seen how to deploy the contract in our local hardhat chain but now we want to do it for the Rinkeby test net (the procedure is the same one for the main net but it’s not the scope of the tutorial).

To deploy on the local chain we just need to type on console this command

`npx hardhat run --network localhost scripts/deploy.ts`

To deploy your contracts on another network we just need to change the parameter value of `--network` but before doing that need to do some preparation step.

### Get some ETH from testnet Faucet

Deploying a contact cost `gas` so we need some from a Faucet. Choose one test network and head over to the faucet to get funds.

-   [Kovan faucet](https://faucet.kovan.network/)
-   [Rinkeby faucet](https://faucet.rinkeby.io/)
-   [Goerli faucet](https://goerli-faucet.slock.it/)

In our case, we decided to use **Rinkeby** so go and get some funds over there.

### Choose an Ethereum Provider

In order to deploy our contract, we need a way to interact directly with the Ethereum network. Nowadays we have two possible options:

-   [Infura](https://infura.io/)
-   [Alchemy](https://www.alchemy.com/)

In our case, we are going to use Alchemy, so go over there, create an account, create a new app and grab the Rinkeby API Key.

### Update the Hardhat Config file

Install the `dotenv` dependencies, this nodejs library allow us to create a `.env` environment file to store our variables without exposing them to the source code.

`yarn add --dev dotenv`

Now create in the root of your project a `.env` file and paste all the information you have collected

```
RENKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/<YOUR_ALCHEMY_APP_ID>
PRIVATE_KEY=<YOUR_BURNER_WALLET_PRIVATE_KEY>
```

**I can’t stress enough. Don’t use your main wallet private case for testing purposes. Create a separate wallet or use a burning wallet to do this type of testing!**

The last step is to update the `hardhat.config.ts` file to add `dotenv` and update the rinkeby information.

At the top of the file add `require("dotenv").config();` and now update the config like this

```ts
const config: HardhatUserConfig = {
  solidity: '0.8.4',
  networks: {
    rinkeby: {
      url: process.env.RENKEBY_URL || '',
      accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
    },
  },
};
```

Are we ready? Let’s deploy it!

`npx hardhat run --network rinkeby scripts/deploy.ts`

If everything goes well you should see something like this in your console

![Deploy on Rinkeby](https://cdn-images-1.medium.com/max/1600/1*EmsX8noYLWA9anxERpz1dA.png)

If you copy that contract address and past it on Etherscan Rinkeby you should be able to see it live! Mine is located here: [0xAf688A3405531e0FC274F6453cD8d3f3dB07D706](https://rinkeby.etherscan.io/address/0xAf688A3405531e0FC274F6453cD8d3f3dB07D706)

### Get your contract verified by Etherscan

> Source code verification provides **transparency** for users interacting with smart contracts. By uploading the source code, Etherscan will match the compiled code with that on the blockchain. Just like contracts, a “smart contract” should provide end users with more information on what they are “digitally signing” for and give users an opportunity to audit the code to independently verify that it actually does what it is supposed to do.

So let’s do it. It’s really an easy step because Hardhat provide a specific Plugin to do that: [hardhat-etherscan](https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html).

Let’s install `yarn add --dev @nomiclabs/hardhat-etherscan`

and include it in your `hardhat.config.ts` adding `import "@nomiclabs/hardhat-etherscan";` at the end of your imports.

The next step is to register an account on Etherscan and [generate an API Key](https://etherscan.io/myapikey).

Once you have it we need to update our `.env` file adding this line of code

```
ETHERSCAN_API_KEY=<PAST_YOU_API_KEY>
```

and update the `hardhat.config.ts` adding the Etherscan config needed by the plugin.

The last thing to do is to run the `verify` task that has been added by the plugin:

`npx hardhat verify --network rinkeby <YOUR_CONTRACT_ADDRESS>`

The plugin have a lot of configurations and different options so check out their documentation if you want to know more.

If everything goes well you should see something like this in your console

![Contract verified by Etherscan](https://cdn-images-1.medium.com/max/2400/1*YO7jpoi2X8sC_uoV8Hye0A.png)

Contract verified by Etherscsan

If you go over the Etherscan contract page again you should see a green checkmark on top of the Contract section. Well done!

## Conclusion and next steps

If you want to use this `solidity-template` to kickstart you next smart contract project just head over to the GitHub repo [https://github.com/StErMi/solidity-template](https://github.com/StErMi/solidity-template) and it the “Use this template” green button and boom, you are ready to go!

This is just the start, I’m planning to add more features in time like for example:

-   Support to eslint for the typescript testing lib
-   Support for [hardhat-deploy](https://hardhat.org/plugins/hardhat-deploy.html), an Hardhat Plugin to better manage deployment
-   Add support to [solidity-coverage](https://hardhat.org/plugins/solidity-coverage.html) plugin
-   Add support to [hardhat-gas-reporter plugin](https://hardhat.org/plugins/hardhat-gas-reporter.html)
-   TBD

Do you have any feedback or do you want to contribute to the Solidity Template? Just create a Pull Request on the GitHub page and let’s discuss it!

## Did you like this content? Follow me for more!

-   GitHub: [https://github.com/StErMi](https://github.com/StErMi)
-   Twitter: [https://twitter.com/StErMi](https://twitter.com/StErMi)
-   Medium: [https://medium.com/@stermi](https://medium.com/@stermi)
-   Dev.to: [https://dev.to/stermi](https://dev.to/stermi)