11 min read
·3 years ago
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:
Create a basic HTML web page
Create a basic Solidity smart contract
Connect the web page with the smart contracts using Viem
Prerequisites
Download and setup MetaMask if you do not have it already
Click on the
User Icon
at the top-right of MetaMask, go toSettings
, and turn onShow Test Networks
Click on
Ethereum Mainnet
at the top of MetaMask and change it toSepolia test network
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.
Install a code editor (We recommend Visual Studio Code)
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)
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.sol
and 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.
Make sure your MetaMask is set to the
Sepolia Test Network
Compile the code using the Compiler tab
Deploy the contract under the "Deploy and Run Transactions" tab
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:
Add
viem
as an external library to your codeUse
viem
to create references to the deployed Solidity contractCall 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-explanatorytype
:function
, self-explanatoryoutputs
: should be[]
because this does not return anythingstateMutability
: This isnonpayable
because this function does not accept Etherinputs
: this is an array of inputs to the function. Each object in the array should haveinternalType
,name
andtype
, and these arestring
,_mood
andstring
respectively
For getMood
, we describe each field below:
name
:getMood
, self-explanatorytype
:function
, self-explanatoryoutputs
: this has the same type as inputs in setMood. ForinternalType
,name
andtype
, this should bestring
,""
, andstring
respectivelystateMutability
: This isview
because this is a view functioninputs
: 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
Land-basedCo-founder
·8 days ago
I can't find injected provider meta mask
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!!!
Mickyy
·last month
This section needs to be updated, most things have been upgraded
Mickyy
·last month
Or if anyone could help with how they did theirs
teostra
·last month
hi, what is the difference between you place script before html button and after html button
LingarajPatil
·2 months ago
Day 4