Comment créer une DApp sur Morph : Un guide complet
Les DApps fonctionnent sur une blockchain, offrant transparence, sécurité et décentralisation. Morph, avec ses fonctionnalités uniques et son environnement convivial pour les développeurs, est une excellente plateforme pour créer des DApps. Cet article technique vous guidera à travers le processus de développement d'une DApp sur Morph, de la configuration de votre environnement à la mise en ligne de votre application.
Introduction à Morph
Morph est la première solution zkEVM optimiste de couche 2 pour Ethereum, compatible à 100 % avec l'EVM. Développer sur Morph est comme développer sur Ethereum. Si vous avez de l'expérience dans le développement sur Ethereum, vous constaterez que votre code, vos outils et vos dépendances existants sont entièrement compatibles avec Morph.
Prérequis
Pour ce tutoriel, nous allons utiliser Hardhat avec TypeScript pour le Smart Contract.
Initialisation du projet
Ouvrez votre terminal et suivez les étapes suivantes :
Créez un dossier de projet. Commande exemple :
mkdir morphme
Entrez dans le dossier que vous venez de créer :
cd morphme
Pour initialiser le projet, exécutez la commande suivante :
npx hardhat init
Cela va initialiser votre dépôt de contrat intelligent avec Hardhat. Vous verrez l'écran suivant :
Utilisez la flèche vers le bas pour sélectionner TypeScript et appuyez sur Entrée. Vous serez invité à répondre à quelques questions, répondez simplement "oui
".
Attendez la fin de l'installation et ouvrez le dossier dans votre éditeur de code.
Structure du projet
Les projets Hardhat comportent 3 dossiers principaux auxquels il faut prêter attention :
Le dossier
contracts
qui contient vos contrats Solidity.Le dossier
test
qui contient les tests de vos contrats.Le dossier
scripts
qui contient des scripts personnalisés pour des tâches comme le déploiement de contrats. Créez ce dossier s'il n'existe pas.
En plus de ceux-ci, un fichier important est hardhat.config.ts
, qui contient la configuration pour les clés privées et les réseaux blockchain.
Hardhat crée également des fichiers par défaut dans ces dossiers pour vous : Lock.sol
dans le dossier contracts
et Lock.ts
dans le dossier test
. Vous pouvez les supprimer puisque nous allons les remplacer par nos propres contrats.
Écriture de votre contrat
Nous allons lancer notre token sur le testnet et créer un "faucet" (distributeur) qui permettra à nos utilisateurs de demander une quantité fixe de tokens chaque jour. Pour cela, nous allons coder et lancer deux contrats intelligents.
Un token conforme à la norme ERC20 de la bibliothèque OpenZeppelin. Nous appellerons ce token "iToken".
Un contrat "faucet" qui permet à tout compte de demander quotidiennement une quantité fixe de tokens, appelé "iTokenFaucet".
iToken
Nous allons hériter du contrat ERC20 de la bibliothèque OpenZeppelin.
Installez OpenZeppelin avec la commande suivante :
npm install @openzeppelin/contracts
.Créez le fichier
iToken.sol
dans le dossiercontracts
et collez le code suivant :
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
contract IToken is ERC20, ERC20Permit {
constructor() ERC20("I Token", "IT") ERC20Permit("I Token") {
_mint(_msgSender(), 1_000_000_000 * 1e18);
}
}
- Ce code hérite de la bibliothèque ERC20 d'OpenZeppelin et crée un contrat qui générera 1 milliard de iTokens lors de son déploiement.
Ensuite, nous allons créer notre contrat "faucet" de tokens.
iTokenFaucet
C'est un contrat simple mais intéressant qui permet à tout compte de demander quotidiennement une quantité fixe de iTokens.
- Créez le fichier
iTokenFaucet.sol
dans le dossiercontracts
et collez le code suivant :
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract ITokenFaucet is Ownable {
uint256 public dailyClaim = 1000 * 1e18;
uint256 public claimInterval = 1 days;
mapping(address => uint256) public lastClaimed;
IERC20 public iToken;
constructor(address _iAddress) Ownable(msg.sender) {
iToken = IERC20(_iAddress);
}
error AlreadyClaimed();
event Claimed(address _cliamer, uint256 _amount);
function claim() external {
if ((block.timestamp - lastClaimed[msg.sender]) < claimInterval) {
revert AlreadyClaimed();
}
lastClaimed[msg.sender] = block.timestamp;
SafeERC20.safeTransfer(iToken, msg.sender, dailyClaim);
emit Claimed(msg.sender, dailyClaim);
}
function drain() external onlyOwner {
SafeERC20.safeTransfer(iToken, owner(), iToken.balanceOf(address(this)));
}
}
- Ce contrat importe également des contrats de la bibliothèque OpenZeppelin pour s'assurer que nous utilisons un code sécurisé et audité.
- C'est une bonne pratique d'utiliser des contrats intelligents déjà testés et éprouvés dès que possible.
- En plus des imports, nous avons écrit notre contrat intelligent avec deux méthodes principales :
claim
etdrain
.
claim
peut être appelé par n'importe qui pour demander sa dose quotidienne de iTokens (fixée à 1000, que vous pouvez changer).drain
ne peut être appelé que par le propriétaire du contrat grâce au modificateuronlyOwner
.
Maintenant que nous avons les contrats prêts, l'étape suivante est d'écrire des tests pour eux.
Tests
Écrire des tests pour vos contrats est essentiel, car cela vous permet de vous assurer qu'ils fonctionnent comme prévu. Cela dépend de la précision et de l'exhaustivité de vos tests. En général, pour les contrats intelligents, l'objectif est d'atteindre une couverture de test de 100 %.
Nous allons écrire des tests pour le contrat iTokenFaucet.sol
, car le contrat iToken.sol
ne contient pas beaucoup de notre code, à part celui que nous avons importé d'OpenZeppelin, qui est déjà testé.
- Créez le fichier
iTokenFaucet.ts
dans votre dossiertest
et collez le code suivant :
import {
time,
loadFixture,
} from "@nomicfoundation/hardhat-toolbox/network-helpers";
import { expect } from "chai";
import { ethers } from "hardhat";
export async function deployiTokenFaucet() {
const [owner, otherAccount] = await ethers.getSigners();
const iTokenContract = await ethers.getContractFactory("iToken");
const iToken = await iTokenContract.deploy();
const iTokenFaucet = await ethers.getContractFactory("iTokenFaucet");
const faucet = await iTokenFaucet.deploy(iToken);
await iToken.transfer(await faucet.getAddress(), BigInt(1e24));
return { iToken, owner, otherAccount, faucet };
}
describe("iTokenFaucet", function () {
describe("Deployment", function () {
it("Should Deploy", async function () {
await loadFixture(deployiTokenFaucet);
});
});
describe("Claim iToken", function () {
it("Should Claim iToken", async function () {
const { faucet, otherAccount } = await loadFixture(deployiTokenFaucet);
await expect(faucet.connect(otherAccount).claim())
.emit(faucet, "Claimed")
.withArgs(await otherAccount.getAddress(), await faucet.dailyClaim());
await expect(
faucet.connect(otherAccount).claim()
).revertedWithCustomError(faucet, "AlreadyClaimed");
});
});
});
Ceci est un test minimal pour commencer.
Vous pouvez voir qu'il vérifie que : a. Le contrat peut être déployé. b. L'utilisateur peut demander des tokens.
Écrire des tests en Solidity est incontournable, car cela vous permet de déployer rapidement votre contrat et de vous assurer que le code peut être exécuté et que les méthodes nécessaires peuvent être appelées. N'hésitez pas à ajouter autant de cas de test que vous le souhaitez.
Configuration de Hardhat
Avant de passer à l'exécution des tests, revenons à notre fichier
hardhat.config.ts
pour nous assurer que toutes les configurations sont correctes.Remplacez le contenu de ce fichier par le code suivant. Cette configuration est spécifique à l'actuel testnet Morph :
import * as dotenv from "dotenv";
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
dotenv.config();
const config: HardhatUserConfig = {
solidity: {
version: "0.8.26",
settings: {
viaIR: true,
optimizer: {
enabled: true,
runs: 99999,
},
evmVersion: "london",
},
},
networks: {
morphTestnet: {
url: "https://rpc-quicknode-holesky.morphl2.io",
accounts: [process.env.PRIVATE_KEY!],
gasPrice: 20000000000, // 2 gwei in wei
},
},
etherscan: {
apiKey: {
morphTestnet: "anything",
},
customChains: [
{
network: "morphTestnet",
chainId: 2810,
urls: {
apiURL: "https://explorer-api-holesky.morphl2.io/api? ",
browserURL: "https://explorer-holesky.morphl2.io/",
},
},
],
},
};
export default config;
Vous pouvez voir qu'il y a une importation de la bibliothèque dotenv en haut, que nous devons installer.
Installez-la avec la commande suivante :
npm install dotenv
.Une fois que c'est fait, créez un fichier
.env
et remplacez-le par le contenu suivant.PRIVATE_KEY=YouPrivateKeyHere ITOKEN=AddressOfItokenOnceYouDeploy ITOKEN_FAUCET=AddressOfItokenFaucetOnceYouDeploy
Gardez à l'esprit que le contenu de ce fichier est censé rester privé, alors assurez-vous qu'il ne soit pas envoyé sur votre GitHub.
Vérifiez que votre fichier
.gitignore
contient bien le fichier.env
.Lancer les tests
Après avoir inclus vos tests, exécutez la commande suivante pour les lancer :
npx hardhat test
.Vous devriez voir que tous les tests sont passés avec succès, super !
Déployer le contrat
Enfin, la partie amusante : déployons nos contrats intelligents sur le réseau ultra-rapide MorphL2.
- Nous devons écrire des scripts de déploiement que nous pourrons exécuter pour déployer les contrats. Rappelez-vous, nous devons déployer deux contrats.
Déployer iToken
Créez un fichier dans le dossier scripts et nommez-le
deploy_itoken.ts
. N'hésitez pas à choisir un autre nom si vous le souhaitez.Collez le code suivant dans ce fichier.
import { ethers } from "hardhat";
async function main() {
const shadow = await ethers.deployContract("IToken");
await shadow.waitForDeployment();
const { ...tx } = shadow.deploymentTransaction()?.toJSON();
tx.data = await shadow.getAddress();
console.log(`deployed to ${JSON.stringify(tx, null, 2)}`);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Lorsque vous exécutez ce script, il va déployer le contrat
iTokens
.Voici la commande pour exécuter le script :
npx hardhat run ./scripts/deploy_itoken.ts
.Vous devriez voir le résultat suivant :
Cependant, ce contrat n'a pas encore été déployé sur le réseau Morph. Pour ce faire, nous devons ajouter l'argument
--network
et spécifier le réseau sur lequel nous souhaitons déployer.Exécutez cette commande pour déployer sur le testnet Morph Holesky :
npx hardhat run ./scripts/deploy_itoken.ts --network morphTestnet
.Le testnet
morphTestnet
ici est pris à partir de la configuration danshardhat.config.ts
.
Voici l'adresse que nous avons obtenue après le déploiement : 0x5086D4873B48041D276E40b7bd5644E6C3c0D247
. La vôtre sera différente. Vous pouvez la vérifier ici : Détails de l'adresse Morph Holesky pour 0x5086D4873B48041D276E40b7bd5644E6C3c0D247.
N'oubliez pas de la définir dans votre fichier .env
. Exemple :ITOKEN=0x5086D4873B48041D276E40b7bd5644E6C3c0D247
.
Vérifier le contrat iToken
Lorsque vous vérifiez un contrat, il obtient une coche dans l'explorateur, ce qui augmente la confiance des utilisateurs dans le contrat. Cela permet également d'afficher le code en Solidity lisible.
Exécutez cette commande pour installer le package de vérification Hardhat :
npm install --save-dev @nomicfoundation/hardhat-verify
Ensuite, exécutez cette commande pour vérifier le contrat que vous venez de déployer :
npx hardhat verify [ITOKEN] --network morphTestnet
.
N'oubliez pas de remplacer[ITOKEN]
par l'adresse que vous avez obtenue lors du déploiement du contrat iToken.
Déployer iTokenFaucet
Créez un fichier dans le dossier scripts et nommez-le
deploy_faucet.ts
. N'hésitez pas à lui donner un autre nom si vous le souhaitez.Collez le code suivant dans ce fichier.
import { ethers } from "hardhat";
async function main() {
const shadow = await ethers.deployContract("ITokenFaucet", [
process.env.ITOKEN!,
]);
await shadow.waitForDeployment();
const { ...tx } = shadow.deploymentTransaction()?.toJSON();
tx.data = await shadow.getAddress();
console.log(`deployed to ${JSON.stringify(tx, null, 2)}`);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
Lorsque vous exécutez ce script, il va déployer le contrat iTokenFaucet.
Exécutez cette commande pour déployer sur le testnet Morph Holesky :
npx hardhat run ./scripts/deploy_faucet.ts --network morphTestnet
.
Voici l'adresse que nous avons obtenue après le déploiement : 0xF2E381ee43cdC0CD99f107eBb9820ca27DA0A1BE
. La vôtre sera différente. Vous pouvez la vérifier ici : Détails de l'adresse Holesky pour 0xF2E381ee43cdC0CD99f107eBb9820ca27DA0A1BE.
N'oubliez pas de la définir dans votre fichier .env
. Exemple :ITOKEN_FAUCET=0xF2E381ee43cdC0CD99f107eBb9820ca27DA0A1BE
.
Vérifier le contrat iTokenFaucet
Ensuite, exécutez cette commande pour vérifier le contrat que vous venez de déployer :
npx hardhat verify [ITOKEN_FAUCET] [ITOKEN] --network morphTestnet
.
N'oubliez pas de remplacer[ITOKEN]
par l'adresse obtenue lors du déploiement du contrat iToken.Cela nécessite à la fois iTokenFaucet et iToken, car le contrat iTokenFaucet prend l'adresse du contrat iToken en paramètre lors du déploiement.
Vous pouvez maintenant transférer des tokens au contrat iTokenFaucet et le tester sur le testnet Morph Holesky.
- Visitez l'adresse du contrat iToken, cliquez sur l'icône MetaMask et ajoutez-le à votre MetaMask.
Allez ensuite dans vos tokens MetaMask et vous le verrez là.
Transférez quelques tokens de votre compte vers iTokenFaucet.
Vérifiez sur iTokenFaucet que les tokens ont bien été transférés.
Frontend
Le tutoriel jusqu'à présent s'est concentré sur l'écriture et le déploiement du contrat intelligent, ce qui est honnêtement la partie compliquée. Si vous êtes déjà développeur frontend, il devrait être facile d'écrire une interface simple pour utiliser ce contrat intelligent.
Bibliothèques et Frameworks à Utiliser
React/Next
pour le framework. La plupart des bibliothèques frontend pour l'écosystème Ethereum sont écrites en React, donc c'est toujours un meilleur choix.Rainbowkit
pour la connexion du portefeuille. RainbowkitWagmi
est un kit d'outils de haut niveau pour construire des applications frontend Ethereum. Il fonctionne bien avec Rainbowkit. WagmiViem
est un kit d'outils de bas niveau pour construire des applications frontend Ethereum. Il est utilisé par Wagmi. Viem.
Notez que si vous ne trouvez pas le réseau Morph dans ces bibliothèques, vous pouvez en créer un personnalisé avec Viem comme ceci :
import { defineChain } from 'viem'
export const morph = defineChain({
id: 2810,
name: 'Morph Holesky',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: { http: ['https://rpc-quicknode-holesky.morphl2.io'] },
},
blockExplorers: {
default: { name: 'Blockscout', url: 'https://explorer-holesky.morphl2.io' },
},
})
Conclusion sur le Frontend
Avec ces bibliothèques, vous devriez être capable de construire un frontend qui interagit avec n'importe quel contrat intelligent Ethereum. Lancez-vous un défi et construisez-en un pour le faucet. Commentez avec un lien vers votre application.
Conclusion
Construire sur Morph est fluide et sécurisé. Ce guide vous offre un aperçu complet pour vous aider à développer et déployer efficacement des applications décentralisées sur la blockchain Morph. Visitez le lien du repo attaché pour plus de détails : GitHub Repo
Subscribe to my newsletter
Read articles from Daniel Kambale directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Daniel Kambale
Daniel Kambale
Hello! I’m Daniel, a Web3 developer specializing in Solidity and smart contract development. My journey in the blockchain space is driven by a vision of a fully decentralized world, where technology empowers individuals and transforms industries. As an ambassador at Morph, I’m committed to fostering growth and innovation in this space, helping to shape a future that values transparency and security. Fluent in both English and French, I enjoy connecting with diverse communities and sharing my insights across languages. This is why you’ll find some of my articles in French, while others are in Swahili, as I believe knowledge should be accessible to all. I use my Hashnode blog to document my learning process, explore decentralized solutions, and share practical tutorials on Web3 development. Whether it's diving deep into Solidity, discussing the latest in blockchain, or exploring new tools, I’m passionate about contributing to a decentralized future and connecting with others who share this vision. Let’s build the future together!