從想法到最小可行的 Dapp - Truffle 命令行界面第 1 部分

每個軟件工程師在從頭開始構建應用程序時都會默認使用一系列工具。在傳統的軟件行業中,有很多工具可供選擇,可以互操作以進行端到端開發。在Web3 這個新興行業中,沒有那麼多工具是在考慮互操作性的情況下構建的。

這就是Truffle 為開發者解決的問題。 Truffle 通過在Truffle 套件中創建與Web3 中的其他工具兼容的工具和功能來簡化開發人員體驗。

這是新的Truffle 內容系列的第一部分,我們在其中概述了從一個想法到最小可行dapp 的步驟,展示了Truffle 套件中為任何經驗水平的智能合約開發人員提供的很酷的功能和工具。

在第一部分中,我們將專注於Truffle CLI ,並通過創建智能合約項目、編寫智能合約代碼、編譯、遷移和與智能合約交互來展示Truffle CLI 的所有可能,所有這些都在Truffle CLI 中進行。

在後續的文章中,我們將通過添加自動化測試來構建這個項目,展示使用Truffle 在JavaScript/TypeScript 和Solidity 中添加智能合約測試是多麼容易。稍後,我們還將探索通過Truffle Debugger和console.log 提供給我們的調試選項。

到本系列結束時,您將能夠從頭開始構建dapp,利用Truffle 工具套件中提供的各種工具,包括Ganache 、用於VS Code 擴展的TruffleTruffle 儀表板Truffle boxes等等更多的。

要求

為了能夠跟進這篇文章的內容,您需要滿足以下要求:

  • Node.js v14 - v18

  • Windows、Linux 或macOS

建議您使用Node 版本管理器下載Node.js,以避免使用sudo 下載Truffle 導致權限錯誤。按照此處的說明為您的操作系統下載Node 版本管理器。

安裝Node.js 後,您可以使用命令npm install -g truffle 全局安裝Truffle。有關更多信息,請參閱Truffle 安裝指南

使用Truffle Init 創建一個Truffle 項目

要使用Truffle 創建智能合約項目,有兩個選項可供選擇,一個是Truffle box選項,我們將在本系列後面詳細介紹。第二個選項是Truffle CLI 選項。對於這篇文章,我們將使用Truffle CLI 選項。

通過在全球範圍內下載Truffle,您可以使用Truffle CLI。為您的項目創建一個空文件夾並將其命名為daily-nft,然後在這個新文件夾的根目錄中運行truffle init。這應該創建一個不包含智能合約的準Truffle 項目。

如果您檢查新創建的項目結構,您會發現以下項目:

contracts/:Solidity 合約目錄

migrations/:可編寫腳本的部署文件的目錄

test/:用於測試應用程序和合約的測試文件目錄

truffle-config.js:松露配置文件

智能合約

對於智能合約,我們將使用我出色的同事Josh構建的這個項目。它被稱為Daily NFT。該項目背後的想法是,根據拍賣的獲勝者,每天都會展示一個NFT。用戶可以在顯示當天的NFT 的同時繼續競標第二天的NFT。

正如您將看到的,這是一個簡單的項目,易於理解,但也足夠複雜,不能成為一個hello world 或計時器項目。

在contracts/目錄下,新建一個Solidity 文件並命名為Auction.sol,然後向其中添加以下內容:

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.13;

contract Auction {

event Start();

event Bid(address indexed sender, uint amount);

event Withdraw(address indexed bidder, uint amount);

event End(address winner, uint amount);

address payable public seller;

uint public endAt;

bool public started;

bool public ended;

address public highestBidder;

uint public highestBid; // Wei

mapping(address => uint) public bids;

mapping(address => address) public nfts;

mapping(address => uint256) public nftIds;

}

對代碼中發生的事情的簡要描述。我們定義了四個事件,它們將在合約的四個重要階段觸發; 我們何時Start 拍賣- 手動、何時Bid、何時觸發Withdraw以及何時拍賣在24 小時內End。

我們還定義了一些公共變量,如下所述:

seller:拍賣所有者的支付地址- 部署地址。

endAt::拍賣結束後的小時數- 24 小時。

started: 表示拍賣是否開始的True 或False 值。

ended:真值或假值,表示拍賣是否結束。

highestBidder:當前最高出價者的地址。

highestBid:以Wei表示的最高出價金額。

bids:地址與其各自出價金額之間的映射

nfts:地址和他們各自的出價nft地址之間的映射

nftIds:nft 地址和它們各自的Id 之間的映射。

...

constructor(

uint _startingBid

) {

seller = payable(msg.sender);

highestBid = _startingBid;

}

function start() external {

require(!started, "started");

require(msg.sender == seller, "not seller");

started = true;

endAt = block.timestamp + 1 days;

emit Start();

}

function bid() external payable {

require(started, "not started");

require(block.timestamp < endAt, "ended");

require(msg.value > highestBid, "value < highest");

require(nfts[msg.sender] != address(0), "include nft to display");

if (highestBidder != address(0)) {

bids[highestBidder] += highestBid;

}

highestBidder = msg.sender;

highestBid = msg.value;

emit Bid(msg.sender, msg.value);

}

// Overload if the user submits an NFT

function bid(address nft, uint256 nftId) external payable {

require(started, "not started");

require(block.timestamp < endAt, "ended");

require(msg.value > highestBid, "value < highest");

if (highestBidder != address(0)) {

bids[highestBidder] += highestBid;

}

if (nfts[msg.sender] == address(0)) {

require(nft != address(0), "invalid nft address");

nfts[msg.sender] = nft;

nftIds[msg.sender] = nftId;

}

highestBidder = msg.sender;

highestBid = msg.value;

emit Bid(msg.sender, msg.value);

}

function withdraw() external {

uint bal = bids[msg.sender];

bids[msg.sender] = 0;

payable(msg.sender).transfer(bal);

emit Withdraw(msg.sender, bal);

}

function end() external {

require(started, "not started");

require(block.timestamp >= endAt, "end time in future");

require(!ended, "ended");

ended = true;

emit End(highestBidder, highestBid);

}

以下是合約代碼第二部分的摘要:

  • constructor() 函數在部署期間被調用,將賣家和highestBid 分別設置為部署者的地址和提供的_startingBid。

  • start() 函數被手動調用以開始拍賣。它將拍賣結束時間設置為從開始時間算起的24 小時。

  • 有兩個bid() 函數,一個接受nft 地址和Id,而另一個不接受任何參數。他們確保新出價的價值高於當前最高出價才能被接受。

  • withdraw()函數允許用戶收回他們的出價金額。

  • end()函數在停止拍賣之前檢查是否已達到拍賣結束時間。

使用Truffle CLI 編譯合約

要編譯新創建的合約,請導航到項目根目錄並運行truffle compile命令。 Truffle 將遍歷/contracts目錄,編譯以.sol 結尾的每個文件和庫。在我們的例子中,我們只有一個合約文件需要編譯。

從想法到最小可行的 Dapp - Truffle 命令行界面第 1 部分

另請注意,在第一次運行時,將編譯所有合約,但在後續運行中,Truffle 將僅編譯自上次編譯以來已更改的合約。您可以通過使用--all選項運行上述命令來覆蓋此行為。

編譯成功後,請注意已build一個新的構建目錄。該目錄包含編譯的工件,特別是在build/contracts/ 目錄中,相對於您的項目根目錄。繼續檢查此目錄的內容。

使用Truffle CLI 在本地部署合約

Truffle 附帶一個內置的個人區塊鏈,可用於在本地與智能合約進行交互和測試。該區塊鏈在您的系統本地,不與以太坊主網絡或測試網絡交互。要訪問它,只需運行命令truffle develop。

為了能夠使用Truffle 將合約部署到任何以太坊網絡,您必須為該合約創建一個遷移文件。遷移是JavaScript 文件,假設您的部署需求會隨時間變化而編寫,可幫助您將合約部署到任何以太坊網絡,包括本地運行的節點,如Truffle 附帶的節點。

現在在migrations/文件夾中,創建一個新文件並將其命名為1_migration.js。 “_migration”之前的數字“1”非常重要,因為它表示順序,即它告訴Truffle 在另一個遷移文件之前運行哪個遷移文件。每個添加到遷移文件夾的新文件都會增加此數字。

將以下內容複製到新創建的遷移文件中:

const Auction = artifacts.require("Auction");

module.exports = function (deployer) {

deployer.deploy(Auction, 100);

};

在這裡,我們使用artifacts.require()函數來告訴Truffle 我們想要與哪個合約文件進行交互。它返回一個合約抽象,我們可以在部署腳本的其餘部分中使用它。

在第二行,我們現在導出一個函數,該函數在調用時採用部署程序對象並調用deployer.deploy()函數來部署合約,同時傳入拍賣合約構造函數中指定的所需參數。如需更詳細地了解遷移在Truffle 中的工作方式,請訪問Truffle 遷移文檔

完成遷移文件後,我們準備將智能合約部署到Truffle 內置的本地區塊鏈。首先運行命令truffle develop 以公開Truffle 開發人員控制台——如果您還沒有運行它的話。

從想法到最小可行的 Dapp - Truffle 命令行界面第 1 部分

請注意,它在端口9545 上啟動了一個本地區塊鏈。此命令還公開了10 個以太坊帳戶以及相關的私鑰和助記詞。確保不要向這些地址發送真實的Eth 代幣,因為它們不安全並且僅用於與您的智能合約進行交互。

在此控制台中,運行命令migrate 以將您的合約部署到本地區塊鏈節點。這應該返回已部署合約的交易ID 和地址,包括成本摘要。

從想法到最小可行的 Dapp - Truffle 命令行界面第 1 部分

使用Truffle CLI 與本地部署的合約交互

將合約部署到Truffle 的本地區塊鏈後,由於我們還沒有dapp 的前端,我們可以直接從truffle develop 命令公開的開發人員控制台手動與部署的智能合約進行交互。讓我們開始吧!

獲取已部署的合約:

truffle(develop)> let auction = await Auction.deployed()

獲取當前設置的賣家和最高出價

truffle(develop)> let seller = await auction.seller()

truffle(develop)> let highestBid = await auction.highestBid()

顯示賣家和最高出價,在我的例子中:

truffle(develop)> seller

'0x1a33B6853b36F4c2E3872E229F3a77Bf75943F9d'

truffle(develop)> highestBid.toNumber()

100

檢查拍賣是否已經開始:

truffle(develop)> let started = await auction.stared()

truffle(develop)> started

false

開始拍賣:

truffle(develop)> await auction.start()

truffle(develop)> started = await auction.started()

true

下一步是什麼?

您剛剛成功創建了一個新的Truffle 項目,編寫了智能合約,編譯並部署了智能合約!我們甚至已經使用Truffle 開發人員控制台在本地與部署的合約進行交互。恭喜!

在下一篇文章中,我們將探索使用Truffle CLI 為我們的智能合約編寫和運行自動化測試。

在Truffle,我們始終致力於通過創建開發人員工具、資源和教育材料來改善和簡化Web3 生態系統中dapp 開發人員的用戶體驗。

要了解有關我們的開發人員工具套件的更多信息,請訪問Truffle 官方網站。如果您有任何疑問,請隨時在我們的Github 討論頁面上開始討論。