智能合約是在區(qū)塊鏈上運(yùn)行并在 web3 生態(tài)系統(tǒng)中啟用去中心化應(yīng)用程序 (dapps) 的自動(dòng)執(zhí)行協(xié)議。Web3 是指下一代互聯(lián)網(wǎng)的術(shù)語(yǔ),用戶(hù)可以更好地控制自己的數(shù)據(jù)、身份和資產(chǎn),并且可以在沒(méi)有中介的情況下直接相互交互。
本篇文章將介紹如何使用 JavaScript 和 Solidity(最流行的 web3 開(kāi)發(fā)語(yǔ)言)與 web3 創(chuàng)建智能合約。以及使用一些工具和平臺(tái)來(lái)簡(jiǎn)化智能合約的編寫(xiě)、部署和交互過(guò)程。
前置條件
要開(kāi)始,您將需要以下內(nèi)容:
-
Visual Studio Code:一個(gè)代碼編輯器,支持 web3 開(kāi)發(fā)的語(yǔ)法高亮、調(diào)試和擴(kuò)展。
-
Ganache:一個(gè)本地區(qū)塊鏈模擬器,允許您在不花費(fèi)真實(shí)以太幣的情況下測(cè)試您的智能合約。
-
Node.js:一種運(yùn)行時(shí)環(huán)境,可讓您在瀏覽器之外運(yùn)行 JavaScript 代碼。
-
Web3.js:一個(gè)提供與以太坊節(jié)點(diǎn)和智能合約交互的接口的庫(kù)。
-
Solidity:一種編程語(yǔ)言,專(zhuān)為在以太坊和其他區(qū)塊鏈上編寫(xiě)智能合約而設(shè)計(jì)。
您可以按照各自網(wǎng)站上的說(shuō)明或使用 npm 等包管理器來(lái)安裝這些工具。
編寫(xiě)智能合約
智能合約是一段代碼,它定義了兩方或多方之間協(xié)議的邏輯和規(guī)則。例如,智能合約可以表示采購(gòu)訂單、保險(xiǎn)單或游戲。
要編寫(xiě)智能合約,我們將使用 Solidity,它類(lèi)似于 JavaScript,但具有一些用于區(qū)塊鏈開(kāi)發(fā)的特定功能和語(yǔ)法。Solidity 文件具有 .sol 擴(kuò)展名,并以指定編譯器版本的 pragma 語(yǔ)句開(kāi)頭。
讓我們編寫(xiě)一個(gè)簡(jiǎn)單的智能合約,代表買(mǎi)賣(mài)雙方之間的采購(gòu)訂單。該合約將具有以下特點(diǎn):
-
它將存儲(chǔ)買(mǎi)家地址、賣(mài)家地址、商品名稱(chēng)和價(jià)格。
-
它將有一個(gè)構(gòu)造函數(shù),在部署合約時(shí)初始化這些值。
-
它將有一個(gè)購(gòu)買(mǎi)功能,允許買(mǎi)家使用以太幣為商品付款。
-
它將有一個(gè)在執(zhí)行購(gòu)買(mǎi)功能時(shí)發(fā)出的事件。
該合約的代碼如下:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// SPDX-License-Identifier: GPL-3.0
// Specify compiler version
pragma solidity ^0.8.0;
// Define contract name
contract PurchaseOrder {
// Declare state variables
address public buyer;
address public seller;
string public item;
uint256 public price;
// Declare event
event Purchased(address buyer, address seller, string item);
// Define constructor function
constructor(address _buyer, address _seller,
string memory _item,
uint256 _price) {
// Assign values to state variables
buyer = _buyer;
seller = _seller;
item = _item;
price = _price;
}
// Define buy function
function buy() public payable {
// Check if sender is buyer
require(msg.sender == buyer,
"Only buyer can buy");
// Check if value is equal to price
require(msg.value == price,
"Value must be equal to price");
// Transfer value to seller
payable(seller).transfer(msg.value);
// Emit event
emit Purchased(buyer,seller,item);
}
}
</code></span></span>
部署智能合約
要在以太坊或任何其他與 web3.js 兼容的區(qū)塊鏈平臺(tái)(例如 Moralis)上部署智能合約,我們需要兩件事:
-
ABI(應(yīng)用程序二進(jìn)制接口):描述如何與合約的功能和事件交互的 JSON 文件。
-
字節(jié)碼:代表合約代碼編譯版本的十六進(jìn)制字符串。
我們可以使用Remix IDE等在線工具或使用 solc-js 生成這些文件,solc-js 是 Solidity 編譯器的 JavaScript 包裝器。
為簡(jiǎn)單起見(jiàn),將在本文中使用 Remix IDE。為此,
-
在瀏覽器中打開(kāi) Remix IDE 并創(chuàng)建一個(gè)名為 PurchaseOrder.sol 的新文件。
-
將我們的 PurchaseOrder.sol 代碼復(fù)制并粘貼到其中。
-
單擊左側(cè)面板上的 Solidity Compiler 選項(xiàng)卡并選擇編譯器版本 ^0.8.0。
-
單擊編譯 PurchaseOrder.sol 按鈕。
-
單擊其下方的“編譯詳細(xì)信息”按鈕。
-
將 ABI JSON 復(fù)制并粘貼到另一個(gè)名為 PurchaseOrderABI.json 的文件中。
-
將字節(jié)碼十六進(jìn)制字符串復(fù)制并粘貼到另一個(gè)名為 PurchaseOrderBytecode.txt 的文件中。
現(xiàn)在我們的 ABI 和字節(jié)碼已準(zhǔn)備好部署。
下一個(gè),
-
在瀏覽器中打開(kāi) Ganache,然后單擊“快速啟動(dòng)工作區(qū)”按鈕。這會(huì)將 Remix IDE 連接到 Ganache,并向您顯示 10 個(gè)帳戶(hù),每個(gè)帳戶(hù)有 100 ETH。
-
單擊左側(cè)面板上的 Deploy & Run Transactions 選項(xiàng)卡,然后選擇 Web3 Provider 作為環(huán)境。
-
單擊我們的 PurchaseOrder 合同名稱(chēng)下方的 Deploy 按鈕。這會(huì)將我們的合約部署到 Ganache 并向我們展示它的地址、ABI 和功能。
-
將合同地址復(fù)制粘貼到另一個(gè)名為 PurchaseOrderAddress.txt 的文件中。
現(xiàn)在我們已經(jīng)使用 Remix IDE 在 Ganache 上部署了我們的智能合約。
與智能合約交互
要使用 web3.js 與智能合約進(jìn)行交互,我們需要執(zhí)行以下步驟:
-
創(chuàng)建一個(gè)通過(guò) HTTP 提供程序連接到 Ganache 的 web3 實(shí)例。
-
創(chuàng)建一個(gè)使用我們的 ABI 和地址的合約實(shí)例。
-
使用我們的合約實(shí)例調(diào)用或發(fā)送交易到我們的合約函數(shù)。
讓我們編寫(xiě)一個(gè) JavaScript 文件來(lái)執(zhí)行這些步驟并打印出有關(guān)我們采購(gòu)訂單的一些信息。
創(chuàng)建一個(gè)名為 PurchaseOrder.js 的新文件并將以下代碼復(fù)制粘貼到其中:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// Import web3 library
const Web3 = require("web3");
// Import fs library for reading files
const fs = require("fs");
// Read ABI JSON file
const abi = JSON.parse(fs.readFileSync("PurchaseOrderABI.json"));
// Read bytecode hex string file
const bytecode = fs.readFileSync("PurchaseOrderBytecode.txt", "utf8");
// Read contract address file
const address = fs.readFileSync("PurchaseOrderAddress.txt", "utf8");
// Create web3 instance and connect to Ganache
const web3 = new Web3("http://localhost:8545");
// Create contract instance using ABI and address
const contract = new web3.eth.Contract(abi, address);
// Get buyer's address from Ganache (first account)
web3.eth.getAccounts().then(accounts => {
const buyer = accounts[0];
// Get seller's address from Ganache (second account)
const seller = accounts[1];
// Get item name and price from constructor arguments
const item = web3.utils.hexToUtf8(bytecode.slice(-128, -64));
const price = web3.utils.hexToNumber(bytecode.slice(-64));
// Print purchase order details
console.log("Purchase Order Details:");
console.log("Buyer: " + buyer);
console.log("Seller: " + seller);
console.log("Item: " + item);
console.log("Price: " + price + " wei");
// Call buy function using buyer's account and value equal to price
contract.methods.buy().send({from: buyer, value: price})
.then(receipt => {
// Print transaction receipt
console.log("Transaction Receipt:");
console.log(receipt);
// Get purchased event from receipt logs
const event = receipt.events.Purchased;
// Print event details
console.log("Event Details:");
console.log(event.returnValues);
})
.catch(error => {
// Print error message
console.error(error.message);
});
});
</code></span></span>
此代碼將執(zhí)行以下操作:
-
它將導(dǎo)入 web3 庫(kù)和 fs 庫(kù)來(lái)讀取文件。
-
它將讀取我們的 ABI JSON 文件、字節(jié)碼十六進(jìn)制字符串文件和合約地址文件。
-
它將創(chuàng)建一個(gè)通過(guò) HTTP 提供程序連接到 Ganache 的 web3 實(shí)例。
-
它將使用我們的 ABI 和地址創(chuàng)建一個(gè)合約實(shí)例。
-
它將分別從 Ganache 帳戶(hù)和構(gòu)造函數(shù)參數(shù)中獲取買(mǎi)家地址、賣(mài)家地址、商品名稱(chēng)和價(jià)格。
-
它將在控制臺(tái)上打印出這些詳細(xì)信息。
-
它將使用買(mǎi)方賬戶(hù)和等于價(jià)格的價(jià)值作為參數(shù)調(diào)用我們合約的購(gòu)買(mǎi)功能。
-
它將在控制臺(tái)上打印出交易收據(jù),其中包括執(zhí)行購(gòu)買(mǎi)功能時(shí)發(fā)出的 Purchased 事件的事件日志。
-
它會(huì)在控制臺(tái)打印出事件詳情,包括買(mǎi)家地址、賣(mài)家地址和商品名稱(chēng)作為返回值。
要運(yùn)行此代碼,
-
在保存所有文件(PurchaseOrder.sol、PurchaseOrderABI.json、PurchaseOrderBytecode.txt、PurchaseOrderAddress.txt、PurchaseOrder.js)的項(xiàng)目文件夾中打開(kāi)一個(gè)終端窗口。
-
確保 Ganache 正在另一個(gè)終端窗口或?yàn)g覽器選項(xiàng)卡中運(yùn)行。
-
在您的終端窗口中鍵入 node PurchaseOrder.js 并按回車(chē)鍵。
你應(yīng)該看到這樣的東西:
監(jiān)聽(tīng)智能合約事件
智能合約可以發(fā)出事件,通知偵聽(tīng)器有關(guān)合約發(fā)生的某些更改或操作。例如,我們的 PurchaseOrder 合約會(huì)在執(zhí)行購(gòu)買(mǎi)功能時(shí)發(fā)出 Purchased 事件。
要使用 web3.js 監(jiān)聽(tīng)智能合約事件,我們可以使用以下方法之一:
-
contract.once(event [, options], callback):此方法將僅獲取一次事件并返回未定義。它對(duì)于偵聽(tīng)只發(fā)生一次的事件很有用,例如構(gòu)造函數(shù)事件。
-
contract.events.MyEvent([options] [, callback]):此方法將獲取名為“MyEvent”的事件并返回一個(gè) eventEmitter 對(duì)象。它對(duì)于監(jiān)聽(tīng)多次發(fā)生的特定事件很有用,例如 Transfer 事件。
-
contract.events.allEvents([options] [, callback]):此方法將獲取合約發(fā)出的所有事件并返回一個(gè) eventEmitter 對(duì)象。它對(duì)于監(jiān)聽(tīng)合約上發(fā)生的所有事件很有用,無(wú)論它們的名稱(chēng)如何。
-
contract.getPastEvents(event [, options] [, callback]):此方法將獲取塊范圍內(nèi)可用的所有過(guò)去事件并返回事件對(duì)象數(shù)組。它對(duì)于獲取歷史數(shù)據(jù)或分析過(guò)去的交易很有用。
所有這些方法都有相似的參數(shù):
-
event:一個(gè)字符串,指定要監(jiān)聽(tīng)或獲取的事件的名稱(chēng)。對(duì) allEvents 或 getPastEvents 方法使用“*”。
-
options:一個(gè)對(duì)象,它指定了一些用于獲取或監(jiān)聽(tīng)事件的過(guò)濾器或設(shè)置,例如:
-
filter:一個(gè)對(duì)象,允許您通過(guò)索引參數(shù)過(guò)濾事件,例如 {from: "0x123…"} 或 {value: web3.utils.toWei("1", "ether")}。
-
fromBlock:一個(gè)數(shù)字或字符串,指定從中獲取或偵聽(tīng)事件的塊號(hào)。對(duì)當(dāng)前區(qū)塊使用“l(fā)atest”,對(duì)未決交易使用“pending”,對(duì)創(chuàng)世區(qū)塊使用“earliest”。
-
toBlock:一個(gè)數(shù)字或字符串,指定要獲取過(guò)去事件的塊號(hào)。對(duì)當(dāng)前區(qū)塊使用“l(fā)atest”,對(duì)未決交易使用“pending”,對(duì)創(chuàng)世區(qū)塊使用“earliest”。
-
callback:一個(gè)有兩個(gè)參數(shù)的函數(shù):錯(cuò)誤和結(jié)果。如果沒(méi)有發(fā)生錯(cuò)誤,則錯(cuò)誤參數(shù)將為 null,并且結(jié)果將是未定義的(對(duì)于 once 方法)、eventEmitter 對(duì)象(對(duì)于事件方法)或事件對(duì)象數(shù)組(對(duì)于 getPastEvents 方法)。
這些方法返回的事件對(duì)象有一些共同的屬性:
-
event:表示事件名稱(chēng)的字符串。
-
address:一個(gè)字符串,指示發(fā)出事件的合約的地址。
-
transactionHash:一個(gè)字符串,指示觸發(fā)事件的交易的哈希值。
-
blockHash:一個(gè)字符串,表示區(qū)塊鏈中的塊號(hào)
-
blockNumber:一個(gè)數(shù)字,指示發(fā)出事件的塊號(hào)。
-
logIndex:一個(gè)數(shù)字,指示事件在塊中的位置。
-
returnValues:包含事件的非索引參數(shù)值的對(duì)象。
events 方法返回的 eventEmitter 對(duì)象有一些常用的方法:
-
on(type, listener):此方法為給定類(lèi)型的事件(例如“數(shù)據(jù)”、“錯(cuò)誤”、“已更改”或“已連接”)注冊(cè)一個(gè)偵聽(tīng)器函數(shù)。
-
once(type, listener):此方法為給定類(lèi)型的事件注冊(cè)一個(gè)監(jiān)聽(tīng)函數(shù),但只執(zhí)行一次,然后將其刪除。
-
off(type, listener):此方法刪除給定事件類(lèi)型的偵聽(tīng)器函數(shù)。
-
remove all listeners ([type]):此方法刪除所有偵聽(tīng)器或僅刪除指定類(lèi)型的偵聽(tīng)器。
下面看一些代碼,使用這些方法來(lái)監(jiān)聽(tīng)我們的 Purchased 事件。
創(chuàng)建一個(gè)名為 PurchaseOrderEvents.js 的新文件,并將以下代碼復(fù)制粘貼到其中:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>// Import web3 library
const Web3 = require("web3");
// Import fs library for reading files
const fs = require("fs");
// Read ABI JSON file
const abi = JSON.parse(fs.readFileSync("PurchaseOrderABI.json"));
// Read contract address file
const address = fs.readFileSync("PurchaseOrderAddress.txt", "utf8");
// Create web3 instance and connect to Ganache via WebSocket provider
const web3 = new Web3("ws://localhost:8545");
// Create contract instance using ABI and address
const contract = new web3.eth.Contract(abi, address);
// Define a callback function for Purchased event
const purchasedCallback = (error, result) => {
if (error) {
// Print error message
console.error(error.message);
} else {
// Print event details
console.log("Purchased Event:");
console.log(result.returnValues);
}
};
// Listen to Purchased event using events method and callback function
contract.events.Purchased({}, purchasedCallback);
// Listen to Purchased event using once method and callback function
contract.once("Purchased", {}, purchasedCallback);
// Listen to all events using allEvents method and callback function
contract.events.allEvents({}, purchasedCallback);
// Get past Purchased events using getPastEvents method and callback function
contract.getPastEvents("Purchased", {}, purchasedCallback);
</code></span></span>
此代碼將執(zhí)行以下操作:
-
它會(huì)導(dǎo)入用于讀取文件的web3庫(kù)和fs庫(kù)。
-
它將讀取我們的 ABI JSON 文件和合約地址文件。
-
它將創(chuàng)建一個(gè)通過(guò) WebSocket 提供程序連接到 Ganache 的 web3 實(shí)例。請(qǐng)注意,我們?cè)谶@里使用 ws:// 而不是 https:// 協(xié)議,因?yàn)橛嗛喪录枰?WebSocket。
-
它將使用我們的 ABI 和地址創(chuàng)建一個(gè)合約實(shí)例。
-
它將定義一個(gè)接受錯(cuò)誤和結(jié)果參數(shù)的回調(diào)函數(shù)。如果沒(méi)有發(fā)生錯(cuò)誤,錯(cuò)誤參數(shù)將為 null,結(jié)果將是一個(gè)事件對(duì)象?;卣{(diào)函數(shù)將在控制臺(tái)上打印出錯(cuò)誤消息或事件詳細(xì)信息。
-
它將使用不同的方法來(lái)收聽(tīng)我們的 Purchased 事件或獲取過(guò)去的事件。每個(gè)方法都接受一個(gè)空的選項(xiàng)對(duì)象(因?yàn)槲覀儾恍枰魏芜^(guò)濾器)和我們的回調(diào)函數(shù)作為參數(shù)。
要運(yùn)行此代碼,
-
在保存了所有文件(PurchaseOrder.sol、PurchaseOrderABI.json、PurchaseOrderBytecode.txt、PurchaseOrderAddress.txt、PurchaseOrder.js、PurchaseOrderEvents.js)的項(xiàng)目文件夾中打開(kāi)一個(gè)終端窗口。
-
確保 Ganache 在啟用 WebSocket 的另一個(gè)終端窗口或?yàn)g覽器選項(xiàng)卡中運(yùn)行(使用 -w 標(biāo)志)。
-
在您的終端窗口中鍵入 node PurchaseOrderEvents.js 并按回車(chē)鍵。
如您所見(jiàn),我們已經(jīng)使用不同的方法成功地監(jiān)聽(tīng)了 Purchased 事件。請(qǐng)注意,根據(jù)您的用例,每種方法都有自己的優(yōu)點(diǎn)和缺點(diǎn)。例如,
-
events 方法比 getPastEvents 方法更高效和實(shí)時(shí),因?yàn)樗褂?WebSocket 而不是 HTTP 協(xié)議,并且不需要輪詢(xún)新塊。
-
對(duì)于一次性事件,once 方法比events 方法更方便,因?yàn)樗鼒?zhí)行后會(huì)自動(dòng)移除監(jiān)聽(tīng)器,不需要手動(dòng)取消訂閱。
-
對(duì)于多個(gè)事件,allEvents 方法比 events 方法更全面,因?yàn)樗鼤?huì)偵聽(tīng)合約發(fā)出的所有可能事件,并且不需要指定每個(gè)事件名稱(chēng)。
-
對(duì)于歷史數(shù)據(jù),getPastEvents 方法比 events 方法更可靠,因?yàn)樗梢詸z索過(guò)去可能因 WebSocket 連接問(wèn)題或節(jié)點(diǎn)同步問(wèn)題而錯(cuò)過(guò)的事件。
結(jié)論
在本文中,我們學(xué)習(xí)了如何使用 JavaScript 和 Solidity 通過(guò) web3 創(chuàng)建智能合約。我們還學(xué)習(xí)了如何使用 web3.js 庫(kù)和 Ganache 模擬器部署、交互和偵聽(tīng)智能合約事件。我們已經(jīng)看到了 web3 及其相關(guān)技術(shù)的一些優(yōu)點(diǎn)和缺點(diǎn),例如區(qū)塊鏈、加密貨幣、DeFi 和 AI。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-734024.html
Web3 是互聯(lián)網(wǎng)未來(lái)的一個(gè)令人興奮和充滿(mǎn)希望的愿景,用戶(hù)可以在其中更好地控制他們的數(shù)據(jù)、身份和資產(chǎn),并且可以在沒(méi)有中介的情況下直接相互交互。然而,web3 也帶來(lái)了一些需要謹(jǐn)慎應(yīng)對(duì)的挑戰(zhàn)和風(fēng)險(xiǎn)。Web3 仍處于開(kāi)發(fā)和采用的早期階段,因此還有很大的改進(jìn)和創(chuàng)新空間。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-734024.html
到了這里,關(guān)于怎么在Web3中創(chuàng)建智能合約的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!