LearnWeb3 Logo

11 min read

·

3 years ago

+4,000 XP
530
263
36

Build your first decentralized application on Ethereum


Build your first decentralized application on Ethereum

This is a step-by-step tutorial on how to create a simple frontend website, create and deploy a Solidity smart contract, and connect them together. We will be using MetaMask, Remix IDE, and Viem. By the end of this tutorial, you will be able to create a simple frontend with two buttons that can interact with smart contract functions.

The tutorial takes place in three stages:

  1. Create a basic HTML web page

  2. Create a basic Solidity smart contract

  3. Connect the web page with the smart contracts using Viem

Prerequisites

  1. Download and setup MetaMask if you do not have it already

  2. Click on the User Icon at the top-right of MetaMask, go to Settings, and turn on Show Test Networks

  3. Click on Ethereum Mainnet at the top of MetaMask and change it to Sepolia test network

  4. Request some Sepolia Testnet ETH from a faucet like this one - Ethereum Sepolia Faucet | Free 0.1 sETH per day | LearnWeb3 from a wallet that has transactions on mainnet. Transfer those funds to your testnet account by opening metamask, clicking on send and entering its address.

    If you don't have a wallet which has transactions on mainnet, use another faucet.

  5. Install a code editor (We recommend Visual Studio Code)

  6. Install Node.js if you do not have it already (Use the LTS version, as that is stable. The latest version is experimental and may have bugs)

  7. Open up a terminal (Command Prompt on Windows, Terminal on macOS or Linux) and install lite-server so you can run your website

npm install -g lite-server

-g stands for global, which means you will now be able to use lite server for running any future project you build on your PC.

Building the webpage

The first thing we will do is build out a basic webpage.

Create a new folder on your computer called first-dapp, and open that folder in your code editor. If you're using VS Code, you can either right click on the created folder and click "open in VS Code" or you can navigate to the folder using your terminal in VS Code. For example if your VS Code's terminal points to Desktop and that is where you've created the first-dapp folder, you can do the following to open it in VS Code:

cd first-dapp code . -r


The first line cd's (change directory) to first-dapp and the second line tells VS Code to open that folder. The -r is a special option which instructs it to open the folder in the same window instead of creating a new one.


Inside that folder, create a new file - index.html - and open that in your code editor by simply clicking on it.


Each HTML page has some basic boilerplate we need to write to tell the web browser that this is an HTML document. Add the following to index.html.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>LearnWeb3 First dApp</title> </head> <body> <!-- We will add more code here --> </body> </html>


Inside the body tag, we will start writing the rest of our code.


The smart contract we create will be very simple - just reading and writing a value to and from the Sepolia testnet. Let's create a textbox and a few buttons on the page.

Update your body tag to look like this:

<body> <div> <h1>This is my dApp!</h1> <p>Here we can set or get the mood:</p> <label for="mood">Input Mood:</label> <br /> <input type="text" id="mood" /> <button onclick="getMood()">Get Mood</button> <button onclick="setMood()">Set Mood</button> <p id="showMood"></p> </div> </body>


We can also write some CSS inside the head tag to make our website look a little better. This step is optional, but helps!

<head> <!-- pre-existing code --> <style> body { text-align: center; font-family: Arial, Helvetica, sans-serif; } div { width: 20%; margin: 0 auto; display: flex; flex-direction: column; } button { width: 100%; margin: 10px 0px 5px 0px; } </style> </head>


Save this file. Press Ctrl + ` to bring up your terminal if you're working in VS Code and make sure it points to first-dapp . Run the following command:

lite-server


If everything was set up properly, your webpage should now be accessible! Go to http://localhost:3000/ to see your page!

We have our boilerplate frontend website ready!

Build the smart contract

Now it is time to write a simple Solidity smart contract. You can use any editor you like, but for now we recommend using Remix.


Inside Remix, create a new contract file named Mood.soland write the following code:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; contract MoodDiary { string mood; function setMood(string memory _mood) public { mood = _mood; } function getMood() public view returns (string memory) { return mood; } }


Now, let's deploy the contract on the Sepolia testnet

IMPORTANT: Following the previous topics, we assume you have already created a new wallet for development purposes with no funds on Ethereum Mainnet. It is essential you use a development wallet with NO REAL MONEY in it when learning, practicing, and testing your contracts.

  1. Make sure your MetaMask is set to the Sepolia Test Network

  2. Compile the code using the Compiler tab

  3. Deploy the contract under the "Deploy and Run Transactions" tab

  4. Under the Deployed Contracts section, you should now see your contract and be able to test out its functions

Be sure to deploy on Sepolia via Remix under the Injected Provider - MetaMask environment and confirm the deployment transaction in MetaMask.

Now, take note of the address and ABI of the contract you just deployed - we will need this on our frontend.

For doing this, you can copy the contract address by clicking the Copy button next to the deployed contracts pulldown in Remix.


You can copy the ABI under the contract in Remix's Compile tab.

Connect your webpage to your smart contract

Now, let's go back to index.html in your code editor. Here, we will do a few things:

  1. Add viem as an external library to your code

  2. Use viem to create references to the deployed Solidity contract

  3. Call your contract's functions through MetaMask with the help of viem


At the beginning (or end) of your HTML file create a script tag and declare two variables there

<script> //here we are just declaring two variables. We will be assigning them their respective values in the next script var getMood var setMood </script>


In the next script, we'll be importing and using Viem, which is a library for interacting with smart contracts.

<script type="module"> //A Wallet Client is an interface to interact with Ethereum Accounts. //The createWalletClient function sets up a Wallet Client with a given medium. import { createWalletClient, custom, getContract, } from "https://esm.sh/viem"; import { sepolia } from "https://esm.sh/viem/chains"; //space for remaining code in the same script


Below the code we just wrote, we'll be creating a wallet client which will enable our app connect the user's account to Ethereum Sepolia

//code in the above block //create a client that connects the user's account to Ethereum Sepolia const walletClient = createWalletClient({ chain: sepolia, transport: custom(window.ethereum), }); //this will make your wallet extension show you a pop-up requesting you to connect your wallet //accounts will be an array const accounts = await walletClient.requestAddresses(); //get the first address in the accounts array const [address] = accounts; //Replace the following two values //Make sure the MoodContractAddress is in double single/double quotes const MoodContractAddress = "..."; const MoodContractABI = ...; //space for reamining code in the same script


If you are wondering what the Contract ABI is, see this section on Solidity's documentation. We will also go in-depth into ABIs a few lessons later.


For the Contract ABI, we want to specifically navigate to the JSON Section and describe our smart contract in JSON format. Since we have two functions in our contract, this should be an array with two objects:

const MoodContractABI = [ {...}, {...} ];


Each object inside the array should have the following fields: constant, inputs, name, outputs, payable, stateMutability, and type.


For setMood, we describe each field below:

  • name: setMood, self-explanatory

  • type: function, self-explanatory

  • outputs: should be [] because this does not return anything

  • stateMutability: This is nonpayable because this function does not accept Ether

  • inputs: this is an array of inputs to the function. Each object in the array should have internalType, name and type, and these are string, _mood and string respectively


For getMood, we describe each field below:

  • name: getMood, self-explanatory

  • type: function, self-explanatory

  • outputs: this has the same type as inputs in setMood. For internalType, name and type, this should be string, "", and string respectively

  • stateMutability: This is view because this is a view function

  • inputs: this has no arguments so this should be []


Your end result should look something like this:

const MoodContractABI = [{ "inputs": [], "name": "getMood", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "string", "name": "_mood", "type": "string" }], "name": "setMood", "outputs": [], "stateMutability": "nonpayable", "type": "function" } ]


Next, in your code create an instance of your contract so that you can interact with it using your frontend.

//pre-existing code const MoodContractInstance = getContract({ address: MoodContractAddress, abi: MoodContractABI, client: walletClient, }); //space for remaining code


Now we can create assign values to getMood and setMood. They will be javascript functions for calling the two respective smart contract functions

getMood= async function() { //since getMood in our contract is a read function, your wallet won't pop up const mood = await MoodContractInstance.read.getMood(); document.getElementById("showMood").innerText = `Your Mood: ${mood}`; } setMood= async function() { const mood = document.getElementById("mood").value; //setMood in our contract is a write function so your wallet will pop up and will ask you to confirm your transaction, requiring some gas fees. await MoodContractInstance.write.setMood([mood],{ account:address }); } </script>


Finally, update your Buttons such that they call these functions when clicked

<button onclick="getMood()">Get Mood</button> <button onclick="setMood()">Set Mood</button>


Save your code, and now it's time to test it!


Your final code should look something like this:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>LearnWeb3 First dApp</title> </head> <body> <body> <script> //here we are just declaring two variables. We will be assigning them their respective values in the next script var getMood var setMood </script> <script type="module"> //A Wallet Client is an interface to interact with Ethereum Accounts. //The createWalletClient function sets up a Wallet Client with a given medium. import { createWalletClient, custom, getContract, } from "https://esm.sh/viem"; import { sepolia } from "https://esm.sh/viem/chains"; //create a client that connects the user's account to Ethereum Sepolia const walletClient = createWalletClient({ chain: sepolia, transport: custom(window.ethereum), }); // this will make your wallet extension show you a pop-up requesting you to connect your wallet // accounts will be an array const accounts = await walletClient.requestAddresses(); //get the first address in the accounts array const [address] = accounts; // Replace the following two values. //Make sure the MoodContractAddress is in double single/double quotes const MoodContractAddress = "..."; const MoodContractABI = ...; const MoodContractInstance = getContract({ address: MoodContractAddress, abi: MoodContractABI, client: walletClient, }); getMood= async function() { const mood = await MoodContractInstance.read.getMood(); document.getElementById("showMood").innerText = `Your Mood: ${mood}`; } setMood= async function() { const mood = document.getElementById("mood").value; await MoodContractInstance.write.setMood([mood],{ account:address }); } </script> <div> <h1>This is my dApp!</h1> <p>Here we can set or get the mood:</p> <label for="mood">Input Mood:</label> <br /> <input type="text" id="mood" /> <button onclick="getMood()">Get Mood</button> <button onclick="setMood()">Set Mood</button> <p id="showMood"></p> </div> </body> </body> </html>

Let's try it out!

If you followed the steps properly, your webpage should now be able to call the getMood and setMood functions on your smart contract!

Make sure your web server is still running at http://localhost:3000 - if not, restart lite-server through your Terminal.

Go to your webpage and test your functions and approve the transactions as needed through your MetaMask wallet!

See your contract and transaction information via https://sepolia.etherscan.io/

Open a console ( Ctrl + Shift + I ) in your web browser to see the magic happen as you press those buttons!

Congratulations

Amazing! If you got this far, that means you just made a simple website that interacts with a real, live Ethereum testnet on the internet. This is not something many folks can say they have done!

If you have trouble at any step of the way and cannot figure it out, reach out to us on our Discord Server and ask for help! We'll be sure to help you out!

Were you able to deploy your smart contract and interact with it from the website?

  • Yes

  • No

*You must be signed in to submit quiz
You must be signed in to post comments
User avatar

Land-basedCo-founder

·

8 days ago

I can't find injected provider meta mask

0
User avatar

ManageableLift

·

26 days ago

i cant get any ETH for testnet there are so many restrictions like having atleast 1ETH on main net. i cant use real money i am so new to use and just learning!!!

0
User avatar

Mickyy

·

last month

This section needs to be updated, most things have been upgraded

0
User avatar

Mickyy

·

last month

Or if anyone could help with how they did theirs

0
User avatar

teostra

·

last month

hi, what is the difference between you place script before html button and after html button

0
User avatar

LingarajPatil

·

2 months ago

Day 4

1
BUGG Logo