A Web3 DApp fejlesztés technológiai veremének megértése során meg kell tanulnia a web3 dApp fejlesztés alapvető technológiai veremét, az RPC szerepét a dApp fejlesztésben, valamint a dRPC használatával fiók létrehozását, API-kulcs generálását, végpontok és végpontok elemzését. , adjon hozzá pénzt dRPC-számlájához, és ellenőrizze egyenlegét.
A dRPC szerepe az intelligens szerződések telepítésében, hogy leegyszerűsítse az Ethereum csomópont beállításának folyamatát, megkönnyítve a fejlesztők számára az interakciót és a telepítést egyetlen kódsor segítségével.
Ebben a cikkben egy kávéfizetési intelligens szerződést fog írni, összeállítani, tesztelni és telepíteni az Ethereum Sepolia Testnetre a dRPC végpont és az API kulcs használatával.
A szolgáltatások közé tartozik:
Mossuk be a kezünket.
Hozzon létre egy mappát a gyökérkönyvtárban, és nevezze el contracts
.
Hozzon létre egy fájlt a contracts
mappában, és nevezze el coffee.sol
-nak.
a szilárdságot fogja használni az intelligens szerződés megírásához. A Solidity fájlok
.sol
kiterjesztéssel vannak elnevezve, mivel ez a Solidity forráskód szabványos fájlkiterjesztése.
Adja hozzá a következő forráskódot a coffee.sol
fájlhoz:
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; contract Coffee { uint256 public constant coffeePrice = 0.0002 ether; uint256 public totalCoffeesSold; uint256 public totalEtherReceived; // Custom error definitions error QuantityMustBeGreaterThanZero(); error InsufficientEtherSent(uint256 required, uint256 sent); error DirectEtherTransferNotAllowed(); // Event to log coffee purchases event CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost); // Function to buy coffee function buyCoffee(uint256 quantity) external payable { if (quantity <= 0) { revert QuantityMustBeGreaterThanZero(); } uint256 totalCost = coffeePrice * quantity; if (msg.value > totalCost) { revert InsufficientEtherSent(totalCost, msg.value); } // Update the total coffees sold and total ether received totalCoffeesSold += quantity; totalEtherReceived += totalCost; console.log("Total ether received updated:", totalEtherReceived); console.log("Total coffee sold updated:", totalCoffeesSold); // Emit the purchase event emit CoffeePurchased(msg.sender, quantity, totalCost); // Refund excess Ether sent if (msg.value > totalCost) { uint256 refundAmount = msg.value - totalCost; payable(msg.sender).transfer(refundAmount); } } // Fallback function to handle Ether sent directly to the contract receive() external payable { revert DirectEtherTransferNotAllowed(); } // Public view functions to get totals function getTotalCoffeesSold() external view returns (uint256) { console.log("getTotalCoffeesSold :", totalCoffeesSold); return totalCoffeesSold; } function getTotalEtherReceived() external view returns (uint256) { console.log("getTotalEtherReceived :", totalEtherReceived); return totalEtherReceived; } }
//SPDX-License-Identifier: MIT
: Ez a licencazonosító azt jelzi, hogy a kód a Massachusetts Institute of Technology (MIT) licence alapján van licencelve.
pragma solidity >=0.8.0 <0.9.0;
: Megadja, hogy a kód a 0.8.0 (beleértve) és a 0.9.0 (kizárólagos) közötti Solidity verziókhoz legyen írva. uint256 public constant coffeePrice = 0.0002 ether; uint256 public totalCoffeesSold; uint256 public totalEtherReceived;
coffeePrice
: Állandó értéke 0.0002 ether
.totalCoffeesSold
: Nyomon követi az eladott kávék számát.totalEtherReceived
: Nyomon követi a szerződés által kapott teljes étert.A Solidity egyéni hibái olyan hibaüzenetek, amelyek egy adott használati esetre vannak szabva, nem pedig a programozási nyelv által biztosított alapértelmezett hibaüzenetek . Segíthetnek javítani a felhasználói élményt, és segíthetnek a hibakeresésben és az intelligens szerződések karbantartásában is.
Egyéni hiba meghatározásához a Solidityben a következő szintaxist használhatja:
error
: Ez a kulcsszó egyéni hiba meghatározására szolgál error QuantityMustBeGreaterThanZero(); error InsufficientEtherSent(uint256 required, uint256 sent); error DirectEtherTransferNotAllowed();
QuantityMustBeGreaterThanZero()
: Biztosítja, hogy a mennyiség nagyobb legyen nullánál.InsufficientEtherSent(uint256 required, uint256 sent)
: Biztosítja, hogy a küldött éter elegendő.DirectEtherTransferNotAllowed()
: Megakadályozza a közvetlen Ether átvitelt a szerződésbe.Az esemény a szerződés része, amely a kibocsátáskor tárolja a tranzakciós naplókban átadott argumentumokat. Az események általában arra szolgálnak, hogy az EVM naplózási funkciójával tájékoztassák a hívó alkalmazást a szerződés aktuális állapotáról. Értesítik az alkalmazásokat a szerződésekben végrehajtott változtatásokról, amelyek azután a kapcsolódó logikát futtathatják.
event CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost);
CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost)
: naplózza a kávévásárlásokat.A függvények önálló kódmodulok, amelyek egy adott feladatot hajtanak végre. Megszüntetik ugyanazon kódrészlet újraírásának redundanciáját. Ehelyett a fejlesztők szükség esetén meghívhatnak egy függvényt a programban.
function buyCoffee(uint256 quantity) external payable { if (quantity <= 0) { revert QuantityMustBeGreaterThanZero(); } uint256 totalCost = coffeePrice * quantity; if (msg.value > totalCost) { revert InsufficientEtherSent(totalCost, msg.value); } // Update the total coffees sold and total ether received totalCoffeesSold += quantity; totalEtherReceived += totalCost; console.log("Total ether received updated:", totalEtherReceived); console.log("Total coffee sold updated:", totalCoffeesSold); // Emit the purchase event emit CoffeePurchased(msg.sender, quantity, totalCost); // Refund excess Ether sent if (msg.value > totalCost) { uint256 refundAmount = msg.value - totalCost; payable(msg.sender).transfer(refundAmount); } } receive() external payable { revert DirectEtherTransferNotAllowed(); } function getTotalCoffeesSold() external view returns (uint256) { console.log("getTotalCoffeesSold :", totalCoffeesSold); return totalCoffeesSold; } function getTotalEtherReceived() external view returns (uint256) { console.log("getTotalEtherReceived :", totalEtherReceived); return totalEtherReceived; }
buyCoffee(uint256 quantity) external payable
: Kezeli a kávévásárlást és a következő műveleteket végzi:receive() external payable
: Visszaállítja a közvetlen éteres átutalásokat abban az esetben, ha valaki közvetlenül a szerződés címére küld pénzt.getTotalCoffeesSold() external view returns (uint256)
: Az összes eladott kávét adja vissza.getTotalEtherReceived() external view returns (uint256)
: A teljes fogadott Ether összegét adja vissza.Itt a Hardhat segítségével fogja összeállítani az intelligens szerződést.
Telepítse a Hardhatot a következő parancssor segítségével.
npm install --save-dev hardhat
A sikeres telepítés után az alábbi választ kapja meg.
Ugyanabban a könyvtárban, ahol a hardhat inicializálását ezzel a parancssorral:
npx hardhat init
Válassza Create a Javascript project
lehetőséget a lefelé mutató nyíl gombbal, és nyomja meg az enter billentyűt.
Nyomja meg az Enter billentyűt a gyökérmappába való telepítéshez
Fogadjon el minden felszólítást a billentyűzet y
gombjával, beleértve a @nomicfoundation/hardhat-toolbox
függőségeket is
Az alábbi válasz azt mutatja, hogy sikeresen inicializált
Észre fogja venni, hogy néhány új mappa és fájl került hozzáadásra a projekthez. pl.
Lock.sol
,iginition/modules
,test/Lock.js
éshardhat.config.cjs
. Ne törődj velük.
Az egyetlen hasznos az
iginition/modules
éshardhat.config.cjs
. Később tudni fogja, mire használják őket. Nyugodtan töröljeLock.sol
fájltcontracts
mappából ésLock.js
iginition/modules
mappából .
Állítsa össze a szerződést a következő parancssor segítségével:
npx hardhat compile
Coffee.json
fájlban található az ABI-kód JSON formátumban, amelyet az intelligens szerződéssel való interakció során hív meg. { "_format": "hh-sol-artifact-1", "contractName": "Coffee", "sourceName": "contracts/coffee.sol", "abi": [ { "inputs": [], "name": "DirectEtherTransferNotAllowed", "type": "error" }, { "inputs": [ { "internalType": "uint256", "name": "required", "type": "uint256" }, { "internalType": "uint256", "name": "sent", "type": "uint256" } ], "name": "InsufficientEtherSent", "type": "error" }, { "inputs": [], "name": "QuantityMustBeGreaterThanZero", "type": "error" }, { "anonymous": false, "inputs": [ { "indexed": true, "internalType": "address", "name": "buyer", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "quantity", "type": "uint256" }, { "indexed": false, "internalType": "uint256", "name": "totalCost", "type": "uint256" } ], "name": "CoffeePurchased", "type": "event" }, { "inputs": [ { "internalType": "uint256", "name": "quantity", "type": "uint256" } ], "name": "buyCoffee", "outputs": [], "stateMutability": "payable", "type": "function" }, { "inputs": [], "name": "coffeePrice", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getTotalCoffeesSold", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "getTotalEtherReceived", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalCoffeesSold", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalEtherReceived", "outputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ], "stateMutability": "view", "type": "function" }, { "stateMutability": "payable", "type": "receive" } ], "bytecode": "", "deployedBytecode": "", "linkReferences": {}, "deployedLinkReferences": {} }
Az intelligens szerződés felépítése közben egy automatizált tesztszkript megírása kulcsfontosságú és erősen ajánlott. Kéttényezős hitelesítésként (2FA) működik, amely biztosítja, hogy az intelligens szerződés a várt módon működjön, mielőtt üzembe helyezné az élő hálózaton.
A test
alatt hozzon létre egy új fájlt, és nevezze el Coffee.
cjs. Illessze be a következő kódot a fájlba:
const { loadFixture } = require("@nomicfoundation/hardhat-toolbox/network-helpers.js"); const { expect } = require("chai"); const pkg = require("hardhat"); const ABI = require('../artifacts/contracts/coffee.sol/Coffee.json'); const { web3 } = pkg; describe("Coffee Contract", function () { // Fixture to deploy the Coffee contract async function deployCoffeeFixture() { const coffeeContract = new web3.eth.Contract(ABI.abi); coffeeContract.handleRevert = true; const [deployer, buyer] = await web3.eth.getAccounts(); const rawContract = coffeeContract.deploy({ data: ABI.bytecode, }); // Estimate gas for the deployment const estimateGas = await rawContract.estimateGas({ from: deployer }); // Deploy the contract const coffee = await rawContract.send({ from: deployer, gas: estimateGas.toString(), gasPrice: "10000000000", }); console.log("Coffee contract deployed to: ", coffee.options.address); return { coffee, deployer, buyer, rawContract }; } describe("Deployment", function () { // Test to check initial values after deployment it("Should set the initial values correctly", async function () { const { coffee } = await loadFixture(deployCoffeeFixture); const totalCoffeesSold = await coffee.methods.totalCoffeesSold().call(); const totalEtherReceived = await coffee.methods.totalEtherReceived().call(); expect(totalCoffeesSold).to.equal("0"); expect(totalEtherReceived).to.equal("0"); }); }); describe("Buying Coffee", function () { // Test to check coffee purchase and event emission it("Should purchase coffee and emit an event", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); const quantity = 3; const totalCost = web3.utils.toWei("0.0006", "ether"); // Buyer purchases coffee const receipt = await coffee.methods.buyCoffee(quantity).send({ from: buyer, value: totalCost }); // Check event const event = receipt.events.CoffeePurchased; expect(event).to.exist; expect(event.returnValues.buyer).to.equal(buyer); expect(event.returnValues.quantity).to.equal(String(quantity)); expect(event.returnValues.totalCost).to.equal(totalCost); }); // Test to check revert when quantity is zero it("Should revert if the quantity is zero", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); expect( coffee.methods.buyCoffee(0).send({ from: buyer, value: web3.utils.toWei("0.0002", "ether") }) ).to.be.revertedWith("QuantityMustBeGreaterThanZero"); }); // Test to check if totalCoffeesSold and totalEtherReceived are updated correctly it("Should update totalCoffeesSold and totalEtherReceived correctly", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); const quantity = 5; const totalCost = web3.utils.toWei("0.001", "ether"); await coffee.methods.buyCoffee(quantity).send({ from: buyer, value: totalCost }); const totalCoffeesSold = await coffee.methods.totalCoffeesSold().call(); const totalEtherReceived = await coffee.methods.totalEtherReceived().call(); expect(totalCoffeesSold).to.equal(String(quantity)); expect(totalEtherReceived).to.equal(totalCost); }); }); describe("Fallback function", function () { // Test to check revert when ether is sent directly to the contract it("Should revert if ether is sent directly to the contract", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); expect( web3.eth.sendTransaction({ from: buyer, to: coffee.options.address, value: web3.utils.toWei("0.001", "ether"), }) ).to.be.revertedWith("DirectEtherTransferNotAllowed"); }); }); });
Ez a kód teszteli a Coffee smart contract működését. Tartalmazza a telepítési teszteket, a kávévásárlást és a szerződésbe történő közvetlen Ether-átvitelek kezelését.
Itt van egy bontás:
deployCoffeeFixture
async function deployCoffeeFixture() { const coffeeContract = new web3.eth.Contract(ABI.abi); coffeeContract.handleRevert = true; const [deployer, buyer] = await web3.eth.getAccounts(); const rawContract = coffeeContract.deploy({ data: ABI.bytecode, }); const estimateGas = await rawContract.estimateGas({ from: deployer }); const coffee = await rawContract.send({ from: deployer, gas: estimateGas.toString(), gasPrice: "10000000000", }); console.log("Coffee contract deployed to: ", coffee.options.address); return { coffee, deployer, buyer, rawContract }; }
describe("Deployment", function () { it("Should set the initial values correctly", async function () { const { coffee } = await loadFixture(deployCoffeeFixture); const totalCoffeesSold = await coffee.methods.totalCoffeesSold().call(); const totalEtherReceived = await coffee.methods.totalEtherReceived().call(); expect(totalCoffeesSold).to.equal("0"); expect(totalEtherReceived).to.equal("0"); }); });
totalCoffeesSold
és totalEtherReceived
nullára legyen állítva a telepítés után. describe("Buying Coffee", function () { it("Should purchase coffee and emit an event", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); const quantity = 3; const totalCost = web3.utils.toWei("0.0006", "ether"); const receipt = await coffee.methods.buyCoffee(quantity).send({ from: buyer, value: totalCost }); const event = receipt.events.CoffeePurchased; expect(event).to.exist; expect(event.returnValues.buyer).to.equal(buyer); expect(event.returnValues.quantity).to.equal(String(quantity)); expect(event.returnValues.totalCost).to.equal(totalCost); }); it("Should revert if the quantity is zero", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); expect( coffee.methods.buyCoffee(0).send({ from: buyer, value: web3.utils.toWei("0.0002", "ether") }) ).to.be.revertedWith("QuantityMustBeGreaterThanZero"); }); it("Should update totalCoffeesSold and totalEtherReceived correctly", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); const quantity = 5; const totalCost = web3.utils.toWei("0.001", "ether"); await coffee.methods.buyCoffee(quantity).send({ from: buyer, value: totalCost }); const totalCoffeesSold = await coffee.methods.totalCoffeesSold().call(); const totalEtherReceived = await coffee.methods.totalEtherReceived().call(); expect(totalCoffeesSold).to.equal(String(quantity)); expect(totalEtherReceived).to.equal(totalCost); }); });
CoffeePurchased
eseményt.totalCoffeesSold
és totalEtherReceived
megfelelően frissül-e a vásárlás után. describe("Fallback function", function () { it("Should revert if ether is sent directly to the contract", async function () { const { coffee, buyer } = await loadFixture(deployCoffeeFixture); expect( web3.eth.sendTransaction({ from: buyer, to: coffee.options.address, value: web3.utils.toWei("0.001", "ether"), }) ).to.be.revertedWith("DirectEtherTransferNotAllowed"); }); });
Miután megírta a tesztszkriptet, a következőket fogja tenni :
console.log()
függvényt hívják a Solidity kódból. Használatához importálnia kell hardhat/console.sol
fájlt a szerződés kódjába, így: //SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "hardhat/console.sol"; contract Coffee { //... }
A szerződés teszteléséhez futtassa a következő parancsot a terminálon:
npx hardhat test
Az alábbiakhoz hasonló kimenettel kell rendelkeznie:
Ez azt mutatja, hogy az intelligens szerződés a várt módon működik.
Ha
npx hardhat test
futtatja, az automatikusan lefordítja és teszteli az intelligens szerződést. Kipróbálhatod, és tudasd velem a megjegyzés rovatban.
Itt helyezi üzembe intelligens szerződését a Sepolia Testnet számára. A Testnet lehetővé teszi az intelligens szerződés tesztelését olyan környezetben, amely az Ethereum főhálózatát utánozza jelentős költségek nélkül. Ha jól ismeri a dApp funkcióját, akkor újratelepítheti az Ethereum Mainnetre.
Telepítse a dotenv csomagot és ezeket a függőségeket.
npm install dotenv npm install --save-dev @nomicfoundation/hardhat-web3-v4 'web3@4'
Ezzel hozzáadja a Web3.Js-t és a Dotenv-t a projektjéhez a „node_modules” mappába helyezve.
importálja őket hardhat.config.cjs
fájlba
require('dotenv').config(); require("@nomicfoundation/hardhat-toolbox"); require("@nomicfoundation/hardhat-web3-v4"); const HardhatUserConfig = require("hardhat/config"); module.exports = { solidity: "0.8.24", } };
Hozzon létre egy .env
fájlt a gyökérmappában.
Szerezze be fiókja privát kulcsát MetaMask pénztárcájából és dRPC API-kulcsából.
Tárolja őket az .env
fájljában.
DRPC_API_KEY=your_drpc_api_key PRIVATE_KEY=your_wallet_private_key
Frissítse a hardhat.config.cjs
fájlt, hogy tartalmazza a Sepolia Testnet konfigurációt:
require('dotenv').config(); require("@nomicfoundation/hardhat-toolbox"); require("@nomicfoundation/hardhat-web3-v4"); const HardhatUserConfig = require("hardhat/config"); const dRPC_API_KEY = process.env.VITE_dRPC_API_KEY; const PRIVATE_KEY = process.env.VITE_PRIVATE_KEY; module.exports = { solidity: "0.8.24", networks: { sepolia: { url: `https://lb.drpc.org/ogrpc?network=sepolia&dkey=${dRPC_API_KEY}`, accounts: [`0x${PRIVATE_KEY}`], } } };
Hozzon létre egy új szkriptfájlt az ignition/module
mappában, és nevezze el deploy.cjs
. Adja hozzá a következő kódot az intelligens szerződés üzembe helyezéséhez:
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); const CoffeeModule = buildModule("CoffeeModule", (m) => { const coffee = m.contract("Coffee"); return { coffee }; }); module.exports = CoffeeModule;
Telepítse az intelligens szerződést a következő parancs futtatásával a terminálon:
npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network sepolia
A parancssor futtatása után a rendszer felkéri Confirm deploy to network sepolia (11155111)? (y/n)
, írja be az y
. Sikeres üzembe helyezéskor látnia kell a telepített intelligens szerződés címét a terminálban.
A szerződés címét a deployed_addresses.json
fájlban is elérheti.
Gratulálunk, sikeresen telepítette intelligens szerződését a Sepolia Testnet rendszerbe. 🎉
Ez a cikk megtanította Önnek, hogyan írjon fizetési intelligens szerződést, hogyan tesztelje, fordítsa le és telepítse az intelligens szerződést a hardhat CLI használatával.
A következő cikkben megtudhatja, hogyan kell elkészíteni a dApp kezelőfelületét. Ez a felhasználói felület a következőkből áll majd:
Az alapértelmezett üzeneteken túl: Az egyéni hibák elsajátítása a Solidityben
Mik azok a szilárdsági függvények?
Mik azok az események a Solidityben?