Ievads Izprotot Web3 DApp izstrādes tehnoloģiju kopu, jums ir jābūt apguvušam galveno tehnoloģiju kopumu Web3 dApp izstrādei, RPC lomu dApp izstrādē un to, kā izmantot dRPC, lai izveidotu kontu, ģenerētu API atslēgu, galapunktus, galapunktu analīzi. , pievienojiet līdzekļus savam dRPC kontam un pārbaudiet savu bilanci. dRPC loma viedo līgumu izvietošanā ir vienkāršot Ethereum mezgla iestatīšanas procesu, atvieglojot izstrādātāju mijiedarbību un izvietošanu, izmantojot tikai vienu koda rindiņu. Šajā rakstā jūs rakstīsit, apkoposit, pārbaudīsit un izvietosit kafijas maksājumu viedo līgumu Ethereum Sepolia Testnet, izmantojot dRPC galapunktu un API atslēgu. Funkcijas ietver: Apmaksa par kafiju Kafijas cenas pārskatīšana Izgūst kopējo pārdotās kafijas skaitu un kopējo nopelnīto naudas summu Sasmērēsim rokas. Priekšnoteikumi Jau papildiniet savu kontu ar Sepolia Faucet. Jums ir seifs, piemēram, Metamask. Koda redaktors. Jau ir instalētas visas jūsu izvēlētās Js bibliotēkas vai ietvari (piemēram, React.js, Next.js utt.). Ūdens burka. Nepieciešamās tehnoloģijas un rīki Soliditāte. React.js, izmantojot Vite.js(Mašīnraksts) Cietā cepure. Web3.js. Dotenv. dRPC API atslēga un beigu punkts. Jūsu konta privātā atslēga. MetaMask Kafijas maksāšanas viedā līguma rakstīšana Savā saknes direktorijā izveidojiet mapi un nosauciet to par . contracts Izveidojiet failu mapē un nosauciet to . contracts coffee.sol viedā līguma rakstīšanai izmantosit soliditāti. Solidity faili ir nosaukti ar paplašinājumu , jo tas ir Solidity avota koda standarta faila paplašinājums. .sol Pievienojiet šādu avota kodu: 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; } } Pragma : Šis licences identifikators norāda, ka kods ir licencēts saskaņā ar licenci. //SPDX-License-Identifier: MIT Masačūsetsas Tehnoloģiju institūta (MIT) : norāda, ka kods ir rakstīts Solidity versijām no 0.8.0 (ieskaitot) līdz 0.9.0 (izņemot). pragma solidity >=0.8.0 <0.9.0; Stāvokļa mainīgais uint256 public constant coffeePrice = 0.0002 ether; uint256 public totalCoffeesSold; uint256 public totalEtherReceived; : iestatīt kā nemainīgu vērtību . coffeePrice 0.0002 ether : izseko pārdoto kafiju skaitu. totalCoffeesSold : izseko kopējo Ether, kas saņemts saskaņā ar līgumu. totalEtherReceived Pielāgotas kļūdas Solidity pielāgotās kļūdas ir . Tie var palīdzēt uzlabot lietotāja pieredzi, kā arī palīdzēt atkļūdot un uzturēt viedos līgumus. kļūdu ziņojumi, kas ir pielāgoti konkrētam lietošanas gadījumam, nevis noklusējuma kļūdu ziņojumi, ko nodrošina programmēšanas valoda Lai Solidity definētu pielāgotu kļūdu, varat izmantot šādu sintaksi: : šis atslēgvārds tiek izmantots, lai definētu pielāgotu kļūdu error Unikāls nosaukums: kļūdai ir jābūt unikālam nosaukumam Parametri: ja vēlaties kļūdas ziņojumā iekļaut konkrētu informāciju vai parametrus, varat tos pievienot iekavās aiz kļūdas nosaukuma. error QuantityMustBeGreaterThanZero(); error InsufficientEtherSent(uint256 required, uint256 sent); error DirectEtherTransferNotAllowed(); : nodrošina, ka daudzums ir lielāks par nulli. QuantityMustBeGreaterThanZero() : nodrošina, ka nosūtītais ēteris ir pietiekams. InsufficientEtherSent(uint256 required, uint256 sent) : novērš tiešu ētera pārsūtīšanu uz līgumu. DirectEtherTransferNotAllowed() Pasākumi Notikums ir līguma daļa, kas saglabā transakciju žurnālos nodotos argumentus, kad tie tiek izvadīti. Notikumi parasti tiek izmantoti, lai informētu zvanīšanas lietojumprogrammu par līguma pašreizējo stāvokli, izmantojot EVM reģistrēšanas funkciju. Viņi paziņo lietojumprogrammām par līgumos veiktajām izmaiņām, kuras pēc tam var izmantot saistītās loģikas palaišanai. event CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost); : reģistrē kafijas pirkumus. CoffeePurchased(address indexed buyer, uint256 quantity, uint256 totalCost) Funkcijas Tie novērš vienu un to pašu koda fragmentu pārrakstīšanu. Tā vietā izstrādātāji var izsaukt funkciju programmā, kad tas ir nepieciešams. Funkcijas ir autonomi koda moduļi, kas veic noteiktu uzdevumu. 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; } : Apstrādā kafijas iegādi un veic šādas darbības: buyCoffee(uint256 quantity) external payable Pārbaudiet, vai daudzums ir derīgs. Aprēķina kopējās izmaksas. Nodrošina pietiekamu ētera nosūtīšanu. Atjaunina stāvokļa mainīgos. Izdod pirkuma notikumu. Atmaksā lieko ēteru. : atgriež tiešos ētera pārskaitījumus, ja kāds tieši nosūta līdzekļus uz līguma adresi. receive() external payable : atgriež kopējo pārdoto kafiju daudzumu. getTotalCoffeesSold() external view returns (uint256) : atgriež kopējo saņemto Ether. getTotalEtherReceived() external view returns (uint256) Kafijas apmaksas viedlīguma sastādīšana Šeit jūs izmantosit Hardhat, lai apkopotu viedo līgumu. Instalējiet Hardhat, izmantojot šo komandu uzvedni. npm install --save-dev hardhat Pēc veiksmīgas instalēšanas jūs saņemsit tālāk sniegto atbildi. Tajā pašā direktorijā, kurā inicializējat hardhat, izmantojot šo komandu uzvedni: npx hardhat init Atlasiet Create a Javascript project izmantojot lejupvērsto bultiņas pogu, un nospiediet taustiņu Enter. Nospiediet enter, lai instalētu saknes mapē taustiņu , tostarp Pieņemiet visas uzvednes, izmantojot tastatūras y @nomicfoundation/hardhat-toolbox atkarības Tālāk ir redzama šī atbilde, kas parāda, ka esat veiksmīgi inicializējies Jūs pamanīsit, ka jūsu projektam ir pievienotas dažas jaunas mapes un faili. piemēram, Lock.sol , iginition/modules , test/Lock.js un hardhat.config.cjs . Neuztraucieties par viņiem. Vienīgie noderīgie ir iginition/modules un hardhat.config.cjs . Vēlāk jūs uzzināsit, kam tie tiek izmantoti. Jūtieties brīvi dzēst Lock.sol mapē contracts un Lock.js mapē iginition/modules . Sastādiet līgumu, izmantojot šādu komandu uzvedni: npx hardhat compile Jūs redzat papildu mapes un failus, piemēram, šo. Failā Coffee.json ir ABI kods JSON formātā, kas tiks izsaukts, mijiedarbojoties ar viedo līgumu. { "_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": {} } Viedā līguma pārbaude Automātiska testa skripta rakstīšana viedā līguma izveides laikā ir ļoti svarīga un ļoti ieteicama. Tas darbojas kā divu faktoru autentifikācija (2FA), nodrošinot, ka viedais līgums darbojas, kā paredzēts, pirms tā izvietošanas tiešraidē. cjs. Failā ielīmējiet šo kodu zemāk: test mapē izveidojiet jaunu failu un nosauciet to Coffee. 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"); }); }); }); Šis kods pārbauda Coffee viedā līguma funkcionalitāti. Tas ietver testus izvietošanai, kafijas pirkšanai un tiešo ētera pārsūtīšanas uz līgumu apstrādi. Šeit ir sadalījums: Fixture funkcija: 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 }; } : izveido jaunu līguma gadījumu un izvieto to, izmantojot izvietotāja kontu. Izvieto kafijas līgumu : novērtē izvietošanai nepieciešamo gāzi. Aplēses par gāzi : izvietotā līguma instance, izvietotāja un pircēja konti. Atgriešana Izvietošanas testi 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"); }); }); : nodrošina, ka un pēc izvietošanas ir iestatītas uz nulli. Pārbauda sākotnējās vērtības totalCoffeesSold totalEtherReceived Kafijas testu pirkšana 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); }); }); : pārbauda, vai kafijas pirkšana atjaunina stāvokli un izdod notikumu . Kafijas iegāde un notikuma izziņošana CoffeePurchased : nodrošina, ka darījums tiek atjaunots, ja daudzums ir nulle. Nulles daudzuma atjaunošana : pārbauda, vai un pēc pirkuma ir pareizi atjaunināti. Atjaunināšanas stāvoklis pareizi totalCoffeesSold totalEtherReceived Atkāpšanās funkcijas tests 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"); }); }); : nodrošina, ka, nosūtot Ether tieši uz līgumu (bez funkcijas izsaukšanas), darījums tiek atjaunots. Tiešā ētera pārsūtīšanas atjaunošana Viedā līguma pārbaude Kad esat uzrakstījis testa skriptu, jūs : Lai to izmantotu, šādi: Palaižot līgumus un testus Hardhat tīklā, varat izdrukāt reģistrēšanas ziņojumus un līguma mainīgos, izsaucot console.log() no sava Solidity koda. līguma kodā ir jāimportē hardhat/console.sol //SPDX-License-Identifier: MIT pragma solidity >=0.8.0 <0.9.0; import "hardhat/console.sol"; contract Coffee { //... } Lai pārbaudītu līgumu, terminālī palaidiet šādu komandu: npx hardhat test Jums vajadzētu būt šādai izvadei: Tas parāda, ka jūsu viedais līgums darbojas tā, kā tas ir paredzēts. Ja palaižat npx hardhat test tas automātiski apkopo un pārbauda viedo līgumu. Varat to izmēģināt un paziņot man komentāru sadaļā. Viedā līguma ieviešana Šeit jūs izvietosit savu viedo līgumu Testnet ļauj pārbaudīt viedo līgumu vidē, kas atdarina Ethereum galveno tīklu, neradot ievērojamas izmaksas. Ja jums ir labas dApp funkcijas, varat to atkārtoti izvietot uz Ethereum Mainnet. Sepolia Testnet. Instalējiet dotenv pakotni un šīs atkarības. npm install dotenv npm install --save-dev @nomicfoundation/hardhat-web3-v4 'web3@4' Tas jūsu projektam pievienos Web3.Js un Dotenv, iekļaujot tos mapē “node_modules”. importējiet tos failā 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", } }; failu. Savā saknes mapē izveidojiet .env Iegūstiet sava konta privāto atslēgu no sava MetaMask maka un dRPC API atslēgas. Saglabājiet tos savā .env failā. DRPC_API_KEY=your_drpc_api_key PRIVATE_KEY=your_wallet_private_key failu Atjauniniet hardhat.config.cjs lai iekļautu Sepolia Testnet konfigurāciju: 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}`], } } }; Izveidojiet jaunu skripta failu zem ignition/module mapē un piešķiriet tam nosaukumu deploy.cjs . Pievienojiet šo kodu, lai izvietotu viedo līgumu: const { buildModule } = require("@nomicfoundation/hardhat-ignition/modules"); const CoffeeModule = buildModule("CoffeeModule", (m) => { const coffee = m.contract("Coffee"); return { coffee }; }); module.exports = CoffeeModule; Izvietojiet viedo līgumu, terminālī izpildot šādu komandu: npx hardhat ignition deploy ./ignition/modules/deploy.cjs --network sepolia Pēc komandu uzvednes palaišanas jums tiks lūgts , ierakstiet . Pēc veiksmīgas izvietošanas terminālī vajadzētu redzēt izvietotā viedā līguma adresi. Confirm deploy to network sepolia (11155111)? (y/n) y Līguma adresei varat piekļūt arī failā . deployed_addresses.json Apsveicam! Jūs esat veiksmīgi izvietojis viedo līgumu Sepolia Testnet. 🎉 Secinājums Šajā rakstā ir aprakstīts, kā uzrakstīt maksājumu viedo līgumu, pārbaudīt, apkopot un izvietot viedo līgumu, izmantojot hardhat CLI. Nākamajā rakstā jūs uzzināsit, kā izveidot šīs dApp priekšējo daļu. Šī lietotāja saskarne sastāvēs no: Ievades lauks iegādāto kafiju skaitam. Poga, kas aktivizē maksājuma darījumu un atskaita to no jūsu konta. Parādīt kopējo nopirkto kafiju un saņemto summu ēteros un USD Pašas kafijas cena gan ēterī, gan USD. Atsauce Papildus noklusējuma ziņojumiem: pielāgoto kļūdu apgūšana stabilitātē Pielāgotu kļūdu apgūšana stabilitātē: paaugstiniet viedos līgumus, pārsniedzot noklusējuma ziņojumus Kas ir stingrības funkcijas? Kas ir Solidity notikumi? ← Iepriekšējais raksts Nākamais raksts →