-
Notifications
You must be signed in to change notification settings - Fork 49
DApp Development: Client‐side Communication
Just like we have communicated through our server-side application, we can do the same from the client's side.
For our session, we will build a SPA (single-page application) using React and Vite.
React is a popular JavaScript library for building user interfaces. It allows developers to create reusable UI components that update efficiently as your application data changes. This component-based approach promotes clean and maintainable code, making React a favourite for building dynamic web applications.
Vite is a next-generation build tool for web projects. It boasts blazing-fast development thanks to on-demand file serving and hot module replacement. Vite also streamlines the build process for production with pre-configured Rollup integration, allowing you to focus on creating amazing web experiences.
To boilerplate a simple Vite project. Follow the given commands.
npm create vite@latestSelect React for the framework and JavaScript for the variant. The project name can be anything.
Install Ethers.js.
npm i ethersNote: Add the Hardhat simulation to the MetaMask and our deployment account.
Unlike the server side, where we use JsonRpcProvider, we can use the BrowserProvider from ethers. As for the connection, we can pass the ethereum object injected by MetaMask in the window.
import { BrowserProvider } from "ethers";
const provider = new BrowserProvider(window.ethereum);Inside the return statement, we add a button with an onClick function connectMetaMask, which will return a signer from the wallet. Using the alert function in client JavaScript, we return the signer address to the user.
async function connectMetaMask() {
const signer = await provider.getSigner();
alert(`Successfully Connected ${signer.address}`);
}
return (
<div>
<button onClick={connectMetaMask}>Connect MetaMask</button>
</div>
)Now, we have to update the app component of React with a form that the user can fill out with desired values. We leverage the useState hook of React to update the state of our React application.
import { useState } from 'react'Here's the final code for creating a form element in React.
function App() {
const [formData, setFormData] = useState({
id: 0,
name: '',
course: '',
grade: '',
date: '',
});
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevState) => ({ ...prevState, [name]: value }));
};
const handleSubmit = (event) => {
event.preventDefault();
console.log(formData);
resetForm();
};
const resetForm = () => {
setFormData({ id: 0, name: '', course: '', grade: '', date: '' });
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="id">ID:</label>
<input
type="text"
id="id"
name="id"
value={formData.id}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="name">Name:</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="course">Course:</label>
<input
type="text"
id="course"
name="course"
value={formData.course}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="grade">Grade:</label>
<input
type="text"
id="grade"
name="grade"
value={formData.grade}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="date">Date:</label>
<input
type="date"
id="date"
name="date"
value={formData.date}
onChange={handleChange}
/>
</div>
<div>
<button type="submit">Submit</button>
<button type="button" onClick={resetForm}>
Reset
</button>
</div>
</form>
);
}
export default AppFirst, we need to import the deployed contract's ABI and Contract Address. The easiest way is to copy the Hardhat artifacts and paste them inside the client src folder.
import { contractAddress } from "./deployed_addresses.json";
import { abi } from "./Cert.json";We must also import the Contract class from ethers to create contract instances.
import { Contract, BrowserProvider } from "ethers";Now, we will update the handleSubmit function to issue the certificate for the data we acquired.
const handleSubmit = async (event) => {
event.preventDefault();
console.log(formData);
const signer = await provider.getSigner()
const instance = new Contract(contractAddress, abi, signer)
const trx = await instance.issue(formData.id, formData.name, formData.course, formData.grade, formData.date)
console.log('Transaction Hash:', trx.hash)
resetForm();
};MetaMask will notify you that the transaction is successful if there are no errors.
To retrieve the certificate data, we have to define two states first. One is to hold our query ID, and the other is to hold the retrieved data.
const [queryID, setQueryID] = useState(0)
const [output, setOutput] = useState("")Now, let's have an input element to set the query ID from the user.
<label htmlFor="queryID">Query ID:</label>
<input
type="number"
id="queryID"
name="queryID"
value={queryID}
onChange={(e) => setQueryID(e.target.value)}
/>Let's have a function getCertificate, which will retrieve the certificate data corresponding to an ID and store it in the output.
const getCertificate = async () => {
const signer = await provider.getSigner()
const instance = new Contract(contractAddress, abi, signer)
const result = await instance.Certificates(queryID)
if (result) {
console.log(result)
setOutput(
`Name: ${result[0]}, Course: ${result[1]}, Grade: ${result[2]}, Date: ${result[3]}`
)
}
}We must assign this function to a button and add another element to display the results.
<div>
<button onClick={getCertificate}> Get Certificate </button>
<p>{output}</p>
</div>That's all for the client-side application. Here's the final code.
import { useState } from 'react'
import { Contract, BrowserProvider } from "ethers";
import { contractAddress } from "./deployed_addresses.json";
import { abi } from "./Cert.json";
function App() {
const [queryI, setQueryID] = useState(0)
const [output, setOutput] = useState("")
const [formData, setFormData] = useState({
id: 0,
name: '',
course: '',
grade: '',
date: '',
});
const provider = new BrowserProvider(window.ethereum)
async function connectMetaMask() {
const signer = await provider.getSigner()
alert(`Successfully Connected ${signer.address}`)
}
const handleChange = (event) => {
const { name, value } = event.target;
setFormData((prevState) => ({ ...prevState, [name]: value }));
};
const handleSubmit = async (event) => {
event.preventDefault();
console.log(formData);
const signer = await provider.getSigner()
const instance = new Contract(contractAddress, abi, signer)
const trx = await instance.issue(formData.id, formData.name, formData.course, formData.grade, formData.date)
console.log('Transaction Hash:', trx.hash)
resetForm();
};
const resetForm = () => {
setFormData({ id: 0, name: '', course: '', grade: '', date: '' });
};
const getCertificate = async () => {
const signer = await provider.getSigner()
const instance = new Contract(contractAddress, abi, signer)
const result = await instance.Certificates(queryID)
if (result) {
console.log(result)
setOutput(
`Name: ${result[0]}, Course: ${result[1]}, Grade: ${result[2]}, Date: ${result[3]}`
)
}
}
return (
<div>
<button onClick={connectMetaMask}>Connect MetaMask</button>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="id">ID:</label>
<input
type="number"
id="id"
name="id"
value={formData.id}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="name">Name:</label>
<input
type="text"
id="name"
name="name"
value={formData.name}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="course">Course:</label>
<input
type="text"
id="course"
name="course"
value={formData.course}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="grade">Grade:</label>
<input
type="text"
id="grade"
name="grade"
value={formData.grade}
onChange={handleChange}
/>
</div>
<div>
<label htmlFor="date">Date:</label>
<input
type="date"
id="date"
name="date"
value={formData.date}
onChange={handleChange}
/>
</div>
<div>
<button type="submit">Submit</button>
<button type="button" onClick={resetForm}>
Reset
</button>
</div>
</form>
<br />
<br />
<div>
<label htmlFor="queryID">Query ID:</label>
<input
type="number"
id="queryID"
name="queryID"
value={queryID}
onChange={(e) => setQueryID(e.target.value)}
/>
</div>
<button
onClick={getCertificate}
>
Get
</button>
<p>{output}</p>
</div>
);
}
export default App- Introduction
- Rise of Ethereum
- Ethereum Fundamentals
- DApps & Smart Contracts
- MetaMask Wallet & Ether
- Solidity: Basics
- Solidity: Advanced
- Solidity Use cases and Examples
- DApp Development: Introduction
- DApp Development: Contract
- DApp Development: Hardhat
- DApp Development: Server‐side Communication
- DApp Development: Client-side Communication
- Advanced DApp Concepts: Infura
- Advanced DApp Concepts: WalletConnect
- Event‐driven Testing
- Interacting with the Ethereum Network
- Tokens: Introduction
- Solidity: Best Practises
- Smart Contract Audit
- Ethereum: Advanced Concepts
- Evolution of Ethereum
- Conclusion