Kutokana na kuelewa rundo la teknolojia kwa ajili ya ukuzaji wa Web3 DApp, lazima uwe umejifunza safu kuu ya teknolojia ya ukuzaji wa web3 dApp, jukumu la RPC katika uundaji wa dApp, na jinsi ya kutumia dRPC kuunda akaunti, kuunda ufunguo wa API, vidokezo, uchanganuzi wa mwisho. , ongeza pesa kwenye Akaunti yako ya dRPC, na uangalie salio lako.
Jukumu la dRPC katika kupeleka mikataba ya smart ni kurahisisha mchakato wa kuanzisha nodi ya Ethereum, ili iwe rahisi kwa watengenezaji kuingiliana na kupeleka kwa mstari mmoja tu wa kanuni.
Katika makala haya, utaandika, kukusanya, kujaribu, na kupeleka mkataba mahiri wa malipo ya kahawa kwa Ethereum Sepolia Testnet kwa kutumia sehemu ya mwisho ya dRPC na ufunguo wa API.
Vipengele ni pamoja na:
Wacha tuchafue mikono yako.
Unda Folda chini ya saraka yako ya mizizi, na uipe jina contracts
.
Unda Faili chini ya folda contracts
, na uipe jina coffee.sol
.
utakuwa unatumia solidity kuandika mkataba wa busara. Faili za Solidity zimepewa jina kwa kiendelezi cha
.sol
kwa sababu ndicho kiendelezi cha kawaida cha faili kwa msimbo wa chanzo wa Solidity.
Ongeza msimbo wa chanzo ufuatao kwa coffee.sol
:
// 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
: Kitambulishi hiki cha leseni kinaonyesha kuwa msimbo umeidhinishwa chini ya Leseni ya Taasisi ya Teknolojia ya Massachusetts (MIT) .
pragma solidity >=0.8.0 <0.9.0;
: Inabainisha kuwa msimbo umeandikwa kwa matoleo ya Solidity kati ya 0.8.0 (pamoja) na 0.9.0 (isiyojumuisha). uint256 public constant coffeePrice = 0.0002 ether; uint256 public totalCoffeesSold; uint256 public totalEtherReceived;
coffeePrice
: Weka kama thamani isiyobadilika ya 0.0002 ether
.totalCoffeesSold
: Hufuatilia idadi ya kahawa zinazouzwa.totalEtherReceived
: Hufuatilia jumla ya Etheri iliyopokelewa na mkataba.Hitilafu maalum katika Solidity ni ujumbe wa hitilafu ambao umeundwa kulingana na hali maalum ya matumizi, badala ya ujumbe wa hitilafu chaguo-msingi ambao hutolewa na lugha ya programu . Wanaweza kusaidia kuboresha matumizi ya mtumiaji, na pia wanaweza kusaidia kutatua hitilafu na kudumisha mikataba mahiri.
Ili kufafanua kosa maalum katika Solidity, unaweza kutumia syntax ifuatayo:
error
: Neno kuu hili linatumika kufafanua hitilafu maalum error QuantityMustBeGreaterThanZero(); error InsufficientEtherSent(uint256 required, uint256 sent); error DirectEtherTransferNotAllowed();
QuantityMustBeGreaterThanZero()
: Inahakikisha kuwa idadi ni kubwa kuliko sifuri.InsufficientEtherSent(uint256 required, uint256 sent)
: Inahakikisha kuwa Etheri iliyotumwa inatosha.DirectEtherTransferNotAllowed()
: Huzuia uhamisho wa moja kwa moja wa Etha kwenye mkataba.Tukio ni sehemu ya mkataba ambayo huhifadhi hoja zilizopitishwa kwenye kumbukumbu za miamala zinapotolewa. Matukio kwa kawaida hutumiwa kufahamisha programu ya kupiga simu kuhusu hali ya sasa ya mkataba kwa kutumia kipengele cha ukataji miti cha EVM. Wanajulisha maombi kuhusu mabadiliko yaliyofanywa kwa kandarasi, ambayo yanaweza kutumika kutekeleza mantiki inayohusiana.
event CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost);
CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost)
: Huweka kumbukumbu za ununuzi wa kahawa.Kazi ni moduli zinazojitosheleza za msimbo zinazotimiza kazi mahususi. Wanaondoa upungufu wa kuandika tena kipande sawa cha msimbo. Badala yake, devs inaweza kuita chaguo za kukokotoa katika programu inapohitajika.
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
: Hushughulikia ununuzi wa kahawa na kutekeleza shughuli zifuatazo:receive() external payable
: Hurejesha uhamisho wa moja kwa moja wa Etha iwapo mtu atatuma pesa kwa anwani ya mkataba moja kwa moja.getTotalCoffeesSold() external view returns (uint256)
: Hurejesha jumla ya kahawa zilizouzwa.getTotalEtherReceived() external view returns (uint256)
: Hurejesha jumla ya Etheri iliyopokelewa.Hapa, utakuwa ukitumia Hardhat kuunda mkataba mzuri.
Sakinisha Hardhat kwa kutumia haraka ya amri ifuatayo.
npm install --save-dev hardhat
Utapata jibu hapa chini baada ya usakinishaji uliofaulu.
Katika saraka ile ile ambapo unaanzisha hardhat kwa kutumia haraka amri hii:
npx hardhat init
Chagua Create a Javascript project
kwa kutumia kitufe cha mshale chini na ubonyeze ingiza.
Bonyeza enter ili kusakinisha kwenye folda ya mizizi
Kubali vidokezo vyote kwa kutumia y
kwenye kibodi yako ikijumuisha utegemezi wa @nomicfoundation/hardhat-toolbox
Unaona jibu hili hapa chini likionyesha kuwa umeanzisha kwa ufanisi
Utagundua baadhi ya folda mpya na faili zimeongezwa kwenye mradi wako. kwa mfano,
Lock.sol
,iginition/modules
,test/Lock.js
nahardhat.config.cjs
. Usijali kuhusu wao.
Ya pekee yenye manufaa ni
iginition/modules
nahardhat.config.cjs
. Utajua wanatumiwa nini baadaye. Jisikie huru kufutaLock.sol
chini ya foldacontracts
naLock.js
chini ya foldaiginition/modules
.
Kusanya mkataba kwa kutumia haraka amri ifuatayo:
npx hardhat compile
Coffee.json
kuna msimbo wa ABI katika umbizo la JSON ambao utauita unapotumia mkataba mahiri. { "_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": {} }
Kuandika hati ya majaribio ya kiotomatiki wakati wa kuunda mkataba wako mahiri ni muhimu na inapendekezwa sana. Hufanya kazi kama uthibitishaji wa vipengele viwili (2FA), kuhakikisha kuwa mkataba wako mahiri unafanya kazi inavyotarajiwa kabla ya kuupeleka kwenye mtandao wa moja kwa moja.
Chini ya folda test
unda faili mpya, na uipe jina Coffee.
cjs. Ndani ya faili, bandika nambari hii hapa chini:
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"); }); }); });
Nambari hii hujaribu utendakazi wa mkataba mahiri wa Kahawa. Inajumuisha vipimo vya kupelekwa, kununua kahawa, na kushughulikia uhamisho wa moja kwa moja wa Etha kwenye mkataba.
Hapa kuna muhtasari:
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
na totalEtherReceived
zimewekwa kuwa sufuri baada ya kupelekwa. 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
.totalCoffeesSold
na totalEtherReceived
zinasasishwa kwa usahihi baada ya ununuzi. 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"); }); });
Baada ya kuandika maandishi ya jaribio, uta :
console.log()
kutoka kwa msimbo wako wa Solidity. Ili kuitumia, lazima uingize hardhat/console.sol
katika msimbo wako wa mkataba kama hii: //SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "hardhat/console.sol"; contract Coffee { //... }
Ili kujaribu mkataba, endesha amri ifuatayo kwenye terminal yako:
npx hardhat test
Unapaswa kuwa na pato kama hili hapa chini:
Hii inaonyesha kuwa mkataba wako mahiri hufanya kazi jinsi inavyotarajiwa.
Ukiendesha
npx hardhat test
itakusanya kiotomatiki na kujaribu mkataba mahiri. Unaweza kujaribu na unijulishe katika sehemu ya maoni.
Hapa, utakuwa unapeleka mkataba wako mahiri kwa Sepolia Testnet. Testnet hukuruhusu kujaribu mkataba wako mahiri katika mazingira ambayo yanaiga mainnet ya Ethereum bila kulipia gharama kubwa. Ikiwa unatumia vyema utendakazi wa dApp, unaweza kisha kusambaza tena kwa Ethereum Mainnet.
Sakinisha kifurushi cha dotenv na utegemezi huu.
npm install dotenv npm install --save-dev @nomicfoundation/hardhat-web3-v4 'web3@4'
Hii itaongeza Web3.Js na Dotenv kwenye mradi wako kwa kuujumuisha kwenye folda ya 'node_modules'.
ziingize kwenye faili yako hardhat.config.cjs
require('dotenv').config(); require("@nomicfoundation/hardhat-toolbox"); require("@nomicfoundation/hardhat-web3-v4"); const HardhatUserConfig = require("hardhat/config"); module.exports = { solidity: "0.8.24", } };
Unda faili ya .env
kwenye folda yako ya mizizi.
Pata ufunguo wa faragha wa akaunti yako kutoka kwa pochi yako ya MetaMask na ufunguo wa API ya dRPC.
Zihifadhi katika faili yako ya .env
.
DRPC_API_KEY=your_drpc_api_key PRIVATE_KEY=your_wallet_private_key
Sasisha faili ya hardhat.config.cjs
ili kujumuisha Usanidi wa Sepolia Testnet:
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}`], } } };
Unda faili mpya ya hati chini ya folda ignition/module
, na ulipe jina deploy.cjs
. Ongeza msimbo ufuatao ili kupeleka mkataba wako mahiri:
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); const CoffeeModule = buildModule("CoffeeModule", (m) => { const coffee = m.contract("Coffee"); return { coffee }; }); module.exports = CoffeeModule;
Sambaza mkataba mahiri kwa kutekeleza amri ifuatayo kwenye terminal yako:
npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network sepolia
Baada ya kutekeleza kidokezo cha amri, utaulizwa Confirm deploy to network sepolia (11155111)? (y/n)
, chapa y
. Unapaswa kuona anwani ya kandarasi yako mahiri uliyoweka kwenye kituo baada ya kutumwa kwa mafanikio.
Unaweza pia kufikia anwani ya mkataba katika faili ya deployed_addresses.json
.
Hongera, umefaulu kupeleka mkataba wako mahiri kwa Sepolia Testnet. 🎉
Makala haya yamekufundisha jinsi ya kuandika mkataba mahiri wa malipo, mtihani, kukusanya na kupeleka kandarasi mahiri kwa kutumia hardhat CLI.
Katika makala inayofuata, utajifunza kujenga mwisho wa mbele wa dApp hii. UI hii itajumuisha:
Zaidi ya Ujumbe Chaguomsingi: Kudhibiti Hitilafu Maalum katika Mshikamano
Kudhibiti Makosa ya Kimila katika Mshikamano: Inua Mikataba Yako Mahiri Zaidi ya Ujumbe Chaguomsingi
Je! ni Matukio gani katika Mshikamano?