Une application en ReactJS pour interagir avec le smart contract
Cet article est la suite de l’article sur la mise en place d’un projet blockchain avec Hardhat.
Dans cet article , nous allon sconstgruire une application ReactJs pour interagir avec le smart contract.
Mise en place de l’application ReactJS. N’oubliez pas d’installer ehters.js
npx create-react-app my-token-frontend cd my-token-frontend npm install ethers
Création de l’interface de contrat
// src/ContractABI.js // This file contains the ABI (Application Binary Interface) for your smart contract export const CONTRACT_ADDRESS = "0x5FbDB2315678afecb367f032d93F642f64180aa3"; // Replace with your actual contract address export const CONTRACT_ABI = [ // Read functions "function name() view returns (string)", "function symbol() view returns (string)", "function decimals() view returns (uint8)", "function totalSupply() view returns (uint256)", "function balanceOf(address) view returns (uint256)", "function allowance(address owner, address spender) view returns (uint256)", // Write functions "function transfer(address to, uint amount) returns (bool)", "function approve(address spender, uint256 amount) returns (bool)", "function transferFrom(address from, address to, uint256 amount) returns (bool)", "function mint(address to, uint256 amount)", // Events "event Transfer(address indexed from, address indexed to, uint amount)", "event Approval(address indexed owner, address indexed spender, uint256 value)" ];
Création du composant App
//src/App.js
import React, { useState, useEffect } from 'react';
import { ethers } from 'ethers';
import { CONTRACT_ADDRESS, CONTRACT_ABI } from './ContractABI';
import './App.css';
function App() {
const [account, setAccount] = useState('');
const [signer, setSigner] = useState(null);
const [contract, setContract] = useState(null);
const [tokenName, setTokenName] = useState('');
const [tokenSymbol, setTokenSymbol] = useState('');
const [balance, setBalance] = useState('0');
const [transferTo, setTransferTo] = useState('');
const [transferAmount, setTransferAmount] = useState('');
const [mintTo, setMintTo] = useState('');
const [mintAmount, setMintAmount] = useState('');
const [isConnected, setIsConnected] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [successMessage, setSuccessMessage] = useState('');
useEffect(() => {
// Check if already connected
checkConnection();
// Listen for account changes
if (window.ethereum) {
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length > 0) {
connectWallet();
} else {
setIsConnected(false);
setAccount('');
}
});
}
}, []);
const checkConnection = async () => {
if (window.ethereum) {
try {
const provider = new ethers.BrowserProvider(window.ethereum);
const accounts = await provider.listAccounts();
if (accounts.length > 0) {
connectWallet();
}
} catch (error) {
console.error("Error checking connection:", error);
}
}
};
const connectWallet = async () => {
setErrorMessage('');
setSuccessMessage('');
setIsLoading(true);
try {
if (!window.ethereum) {
setErrorMessage("MetaMask is not installed. Please install MetaMask to use this application.");
setIsLoading(false);
return;
}
// Request account access
const provider = new ethers.BrowserProvider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const signer = await provider.getSigner();
const address = await signer.getAddress();
const tokenContract = new ethers.Contract(CONTRACT_ADDRESS, CONTRACT_ABI, signer);
setAccount(address);
setSigner(signer);
setContract(tokenContract);
setIsConnected(true);
// Get token info
const name = await tokenContract.name();
const symbol = await tokenContract.symbol();
setTokenName(name);
setTokenSymbol(symbol);
// Get balance
updateBalance(tokenContract, address);
setSuccessMessage("Wallet connected successfully!");
} catch (error) {
console.error("Connection error:", error);
setErrorMessage("Failed to connect wallet: " + error.message);
} finally {
setIsLoading(false);
}
};
const updateBalance = async (tokenContract, address) => {
try {
const balance = await tokenContract.balanceOf(address);
setBalance(ethers.formatEther(balance));
} catch (error) {
console.error("Error getting balance:", error);
}
};
const handleTransfer = async (e) => {
e.preventDefault();
setErrorMessage('');
setSuccessMessage('');
setIsLoading(true);
try {
if (!ethers.isAddress(transferTo)) {
throw new Error("Invalid recipient address");
}
const amount = ethers.parseEther(transferAmount);
const tx = await contract.transfer(transferTo, amount);
await tx.wait();
setSuccessMessage(`Successfully transferred ${transferAmount} ${tokenSymbol} to ${transferTo}`);
setTransferAmount('');
// Update balance
updateBalance(contract, account);
} catch (error) {
console.error("Transfer error:", error);
setErrorMessage("Transfer failed: " + error.message);
} finally {
setIsLoading(false);
}
};
const handleMint = async (e) => {
e.preventDefault();
setErrorMessage('');
setSuccessMessage('');
setIsLoading(true);
try {
if (!ethers.isAddress(mintTo)) {
throw new Error("Invalid recipient address");
}
const amount = ethers.parseEther(mintAmount);
const tx = await contract.mint(mintTo, amount);
await tx.wait();
setSuccessMessage(`Successfully minted ${mintAmount} ${tokenSymbol} to ${mintTo}`);
setMintAmount('');
// Update balance if minted to self
if (mintTo.toLowerCase() === account.toLowerCase()) {
updateBalance(contract, account);
}
} catch (error) {
console.error("Mint error:", error);
setErrorMessage("Mint failed: " + error.message);
} finally {
setIsLoading(false);
}
};
return (
<div className="App">
<header className="App-header">
<h1>{tokenName || 'MyToken'} Interface</h1>
{!isConnected ? (
<button onClick={connectWallet} disabled={isLoading}>
{isLoading ? 'Connecting...' : 'Connect Wallet'}
</button>
) : (
<div className="connected-container">
<p>Connected Account: {account}</p>
<p>Balance: {balance} {tokenSymbol}</p>
<div className="card">
<h2>Transfer Tokens</h2>
<form onSubmit={handleTransfer}>
<div className="form-group">
<label>Recipient Address:</label>
<input
type="text"
value={transferTo}
onChange={(e) => setTransferTo(e.target.value)}
placeholder="0x..."
required
/>
</div>
<div className="form-group">
<label>Amount:</label>
<input
type="text"
value={transferAmount}
onChange={(e) => setTransferAmount(e.target.value)}
placeholder="0.0"
required
/>
</div>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Processing...' : 'Transfer'}
</button>
</form>
</div>
<div className="card">
<h2>Mint Tokens (Owner Only)</h2>
<form onSubmit={handleMint}>
<div className="form-group">
<label>Recipient Address:</label>
<input
type="text"
value={mintTo}
onChange={(e) => setMintTo(e.target.value)}
placeholder="0x..."
required
/>
</div>
<div className="form-group">
<label>Amount:</label>
<input
type="text"
value={mintAmount}
onChange={(e) => setMintAmount(e.target.value)}
placeholder="0.0"
required
/>
</div>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Processing...' : 'Mint'}
</button>
</form>
</div>
</div>
)}
{errorMessage && <p className="error-message">{errorMessage}</p>}
{successMessage && <p className="success-message">{successMessage}</p>}
</header>
</div>
);
}
export default App;
Un peu de style
// src/App.css
.App {
text-align: center;
font-family: Arial, sans-serif;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
font-size: calc(10px + 1vmin);
color: white;
padding: 20px;
}
button {
background-color: #61dafb;
border: none;
color: #282c34;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 10px 2px;
cursor: pointer;
border-radius: 4px;
font-weight: bold;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.connected-container {
width: 100%;
max-width: 800px;
}
.card {
background-color: #3a3f4b;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.form-group {
margin-bottom: 15px;
text-align: left;
}
label {
display: block;
margin-bottom: 5px;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.error-message {
color: #ff6b6b;
background-color: rgba(255, 107, 107, 0.1);
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.success-message {
color: #51cf66;
background-color: rgba(81, 207, 102, 0.1);
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
Lancement du front
npm start

Configuration de MetaMask pour utiliser la blockchain locale
- Network Name: Hardhat Local
- RPC URL: http://127.0.0.1:8545/
- Chain ID: 31337
- Currency Symbol: ETH
L’ajout de Hardhat se fait comme pour tout ajout de blockchain.
Vérifiez que vous êtes bien connecté sur la blockchain locale, ensuite ajoutez un compte à votre wallet, à partir de la clé privée, il y a 20 comptes qui sont listés lorsque vous déployez le smart contract
On arrive sur cet écran :

On peut faire un transfert de token vers une autre addresse
Account #10: 0xBcd4042DE499D14e55001CcbB24a551F3b954096 (10000TH) Private Key: 0xf214f2b2cd398c806f84e317254e0f0b801d0643303237da22a48e01628897
On va vérifier que le compte 10 a bien reçu les tokens. Pour ce faire on va aller dans la shell de Hardhat
npx hardhat console --network localhost

