ኣእዳውኩም ንረክስ።
ኣብ ትሕቲ ሱር ማህደርካ Folder ፍጠር፣ contracts
ዝብል ስም ድማ ስመዮ።
ኣብ ትሕቲ contracts
ዝብል ማህደር ፋይል ፍጠር፣ coffee.sol
ዝብል ስም ድማ ስመዮ።
ነቲ ስማርት ውዕል ንምጽሓፍ solidity ክትጥቀም ኢኻ። ፋይላት ሶሊዲቲ ብ
.sol
ኤክስቴንሽን ዝተሰየሙ ምኽንያቱ ንሶሊዲቲ ምንጪ ኮድ ስታንዳርድ ፋይል ኤክስቴንሽን ስለዝኾነ።
ኣብ 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
: እዚ ናይ ፍቓድ መለለዪ እቲ ኮድ ብመሰረት ፍቓድ ፍቓድ ከም ዘለዎ የመልክት።
pragma solidity >=0.8.0 <0.9.0;
: እቲ ኮድ ኣብ መንጎ 0.8.0 (ሓዊሱ)ን 0.9.0ን (ፍሉይ)ን ንዝርከቡ ስሪት ሶሊዲቲ ከም ዝተጻሕፈ ይገልጽ። uint256 public constant coffeePrice = 0.0002 ether; uint256 public totalCoffeesSold; uint256 public totalEtherReceived;
coffeePrice
: ከም ቀዋሚ ዋጋ 0.0002 ether
ኣቐምጥ ።totalCoffeesSold
: ብዝሒ ዝተሸጡ ቡናት ይከታተል።totalEtherReceived
: ብውዕል ዝተቐበሎ ጠቕላላ ኢተር ይከታተል።ኣብ ሶሊዲቲ ዝርከቡ ብሕታዊ ጌጋታት ብቋንቋ ፕሮግራሚንግ ዝቐርቡ ነባሪ መልእኽትታት ጌጋ ዘይኮነስ፡ ንፍሉይ ኣጠቓቕማ ዝተመጣጠኑ ናይ ጌጋ መልእኽትታት እዮም። ንተመኩሮ ተጠቃሚ ንምምሕያሽ ክሕግዙ ይኽእሉ፣ ከምኡ’ውን ኣብ ምእራምን ምሕላውን ስማርት ውዕላት ክሕግዙ ይኽእሉ።
error
: እዚ ቁልፊ ቃል እዚ ንሓደ ብሕታዊ ጌጋ ንምግላጽ ይጥቀመሉ error QuantityMustBeGreaterThanZero(); error InsufficientEtherSent(uint256 required, uint256 sent); error DirectEtherTransferNotAllowed();
QuantityMustBeGreaterThanZero()
: እቲ ብዝሒ ካብ ዜሮ ከም ዝዓቢ የረጋግጽ።InsufficientEtherSent(uint256 required, uint256 sent)
: እቲ ዝተላእከ ኢተር እኹል ምዃኑ የረጋግጽ።DirectEtherTransferNotAllowed()
: ቀጥታዊ ምስግጋር ኢተር ናብ ውዕል ይኽልክል። event CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost);
CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost)
: ዕድጊ ቡን ይምዝግብ።ተግባራት ንሓደ ፍሉይ ዕማም ዝፍጽሙ ርእሶም ዝኽእሉ ሞዱላት ኮድ እዮም። ተመሳሳሊ ቁራጽ ኮድ ዳግማይ ምጽሓፍ ዝብል ትርፊ የወግዱ። ኣብ ክንድኡስ፡ devs ኣብቲ ፕሮግራም ንዘሎ ፋንክሽን ኣድላዪ ኣብ ዝኾነሉ እዋን ክጽውዑ ይኽእሉ።
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
: ንዕድጊ ቡን ይሕዝን እዞም ዝስዕቡ ስርሓት ይፍጽምን፤receive() external payable
: ሓደ ሰብ ብቐጥታ ናብቲ ናይ ውዕል ኣድራሻ ገንዘብ እንተሰዲዱ ቀጥታዊ ናይ ኢተር ዝውውር ይመልስ።getTotalCoffeesSold() external view returns (uint256)
: ጠቕላላ ዝተሸጡ ቡናት ይመልስ።getTotalEtherReceived() external view returns (uint256)
: ጠቕላላ ዝተቐበለ ኢተር ይመልስ።
ነዚ ዝስዕብ ትእዛዝ መመልከቲ ተጠቒምካ Hardhat ንጽዓኖ።
npm install --save-dev hardhat
ኣብ ተመሳሳሊ ማህደር ነዚ ናይ ትእዛዝ ምልክት ተጠቂምካ hardhat ትጅምረሉ፤
npx hardhat init
ነቲ ፍላጻ ንታሕቲ ዝብል ቁልፊ ብምጥቃም Create a Javascript project
ምረጽ እሞ enter ንጽቀጥ።
ኣብቲ ሱር ማህደር ንምትካል enter ንጽቀጥ
ኩሎም ኣብ ቁልፊ ሰሌዳኻ y
ብምጥቃም ዝቐርቡ ምልክታት ሓዊስካ @nomicfoundation/hardhat-toolbox
ጽግዕተኛታት ተቐበል
እዚ ኣብ ታሕቲ ዘሎ መልሲ ብዓወት ከም ዝጀመርካ ዘርኢ ትርእዮ
ገለ ሓደስቲ ማህደርን ፋይላትን ኣብ ፕሮጀክትካ ከም ዝተወሰኹ ከተስተብህል ኢኻ። ንኣብነት
Lock.sol
፡ iginitioniginition/modules
፡ testtest/Lock.js
ከምኡ’ውንhardhat.config.cjs
። ብዛዕባኦም ኣይትጨነቑ።
እቶም እንኮ ጠቐምቲ ድማ
iginition/modules
ንhardhat.config.cjs
እዮም ። ንእንታይ ከም ዝውዕሉ ድሒርካ ክትፈልጥ ኢኻ።Lock.sol
ኣብ ትሕቲcontracts
folder ከምኡ ውንLock.js
ኣብ ትሕቲiginition/modules
folder ክትድምስሱ ናጻ ትኾኑ ።
ነዚ ዝስዕብ ትእዛዝ ምልክት ተጠቒምካ ነቲ ውዕል ኣወሃህዶ፤
npx hardhat compile
Coffee.json
ፋይል ብJSON ፎርማት ዘሎ ABI ኮድ ኣሎ ምስቲ ስማርት ውዕል ክትራኸብ ከለኻ ክትድውለሉ ኢኻ። { "_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": {} }
ኣብ ትሕቲ እቲ test
ማህደር ሓድሽ ፋይል ፍጠር፣ Coffee.
cjs. ኣብ ውሽጢ እቲ ፋይል ነዚ ኮድ ኣብ ታሕቲ ንለጥፎ :
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: "", }); 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"); }); }); });
እዚ ኮድ እዚ ንኣሰራርሓ ናይቲ ናይ ቡን ስማርት ውዕል ይፍትን። ንሱ ድማ ንምውፋር ዝግበር ፈተነታት፡ ቡን ንምግዛእ፡ ከምኡ’ውን ቀጥታዊ ዝውውር ኢተር ናብ ውዕል ምሕላው የጠቓልል።
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: "", }); 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
ን totalEtherReceived
ን ናብ ዜሮ ከምዝተቐመጡ የረጋግጽ። 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
ን totalEtherReceived
ን ብትኽክል ከምዝተመሓየሹ የረጋግጽ። 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"); }); });
console.log()
ዝጽውዑ ናይ ምዝገባ መልእኽትታትን ናይ ውዕል ተለዋዋጢ ዋጋታትን ክትሕትም ትኽእል። ንኽትጥቀመሉ ድማ ኣብ ናይ ውዕል ኮድካ hardhat/console.sol
ከምዚ ጌርካ ከተእቱ ኣለካ፤ //SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "hardhat/console.sol"; contract Coffee { //... }
ነቲ ውዕል ንምፍታን ኣብ ተርሚናልካ ነዚ ዝስዕብ ትእዛዝ ኣካይድ፤
npx hardhat test
ከምዚ ኣብ ታሕቲ ዘሎ ውጽኢት ክህልወካ ይግባእ፤
npx hardhat test
እንተ ኣካይድካ ብኣውቶማቲክ ነቲ ስማርት ውዕል ኣኪቡ ይፍትኖ። ክትፍትንዎ ትኽእሉ ኢኹም ኣብ ክፍሊ ኮመንት ድማ ክትሕብሩኒ ትኽእሉ ኢኹም።
ነቲ dotenv packageን እዞም ጽግዕተኛታትን ጽዓኑ።
npm install dotenv npm install --save-dev @nomicfoundation/hardhat-web3-v4 'web3@4'
እዚ ድማ ኣብቲ 'node_modules' ዝብል ፎልደር ብምእታው ኣብ ፕሮጀክትካ Web3.Jsን Dotenvን ክውስኸሉ እዩ።
ናብ 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", } };
ኣብ ሱር ማህደርና .env
ፋይል ንፍጠር ።
ካብ ሜታማስክ ቦርሳኻን dRPC ኤፒኣይ መፍትሕካን ናይ ኣካውንትካ ብሕታዊ መፍትሕ ውሰድ።
ኣብ .env
ፋይልካ ኣቐምጦም ።
DRPC_API_KEY=your_drpc_api_key PRIVATE_KEY=your_wallet_private_key
ነቲ hardhat.config.cjs
ፋይል ንSepolia Testnet Configuration ንምሕዋስ ኣዘምኖ ፤
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: `//lb.drpc.org/ogrpc?network=sepolia&dkey=${dRPC_API_KEY}`, accounts: [`0x${PRIVATE_KEY}`], } } };
ኣብ ትሕቲ ignition/module
ዝብል ኣቃፊራ ሓድሽ ስክሪፕት ፋይል ፍጠር ፣ deploy.cjs
ድማ ስመዮ ። ስማርት ውዕልካ ንምውፋር እዚ ዝስዕብ ኮድ ወስኸሉ፤
const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); const CoffeeModule = buildModule("CoffeeModule", (m) => { const coffee = m.contract("Coffee"); return { coffee }; }); module.exports = CoffeeModule;
ኣብ ተርሚናልካ ነዚ ዝስዕብ ትእዛዝ ብምስራሕ ነቲ ስማርት ውዕል ኣዋፍሮ፤
npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network sepolia
ድሕሪ ምዝዋር ኮማንድ ፕሮምፕት Confirm deploy to network sepolia (11155111)? (y/n)
፣ y
ጽሓፍ። ኣብ ዕዉት ምውፋር ኣድራሻ ናይቲ ዝተዋፈረ ስማርት ውዕልካ ኣብቲ ተርሚናል ክትሪኦ ኣለካ።
ኣብቲ deployed_addresses.json
ፋይል እውን ነቲ ናይ ውዕል ኣድራሻ ክትረኽቦ ትኽእል ኢኻ።
እንቋዕ ሓጎሰካ፡ ስማርት ውዕልካ ናብ ሰፖልያ ቴስትነት ብዓወት ኣዋፊርካዮ ኣለኻ። 🎉