使用 Hardhat 部署第一個智能合約
導言
本節將指導你使用 Hardhat 向Kaia-Kairos網絡部署靈魂令牌。
Hardhat 是一個智能合約開發環境,它將為您提供幫助:
- 開發和編譯智能合約。
- 調試、測試和部署智能合約和 dApp。
靈魂綁定令牌(SBT)是不可轉讓的 NFT。 也就是說,一旦獲得,就不得出售或轉讓給其他用戶。 要了解有關 SBT、其工作原理和使用案例的更多信息,可以查看 Vitalik Buterin 發表的這篇 參考文章。
完成本指南後,您將能夠
- 在 Kaia 上建立一個 "Hardhat "項目。
- 創建一個簡單的靈魂綁定令牌。
- 使用 Hardhat 編譯智能合約。
- 使用 Hardhat 測試、部署智能合約並與之交互。
- 探索 Hardhat 分叉功能。
先決條件
學習本教程的前提條件如下:
- 代碼編輯器:源代碼編輯器,如 VS Code。
- Metamask:用於部署合約、簽署事務和與合約交互。
- RPC 端點 :可從支持的端點提供程序中獲取。
- 從 Faucet測試 KAIA:為賬戶注入足夠的 KAIA。
- NodeJS和NPM
設置開發環境
要使用 hardhat,我們需要建立開發環境並安裝 hardhat。 讓我們按以下步驟來做:
第 1步創建項目目錄
mkdir soulbound-tokenscd soulbound-tokens
步驟 2:初始化 npm 項目
在終端中粘貼此命令以創建 package.json 文件
npm init -y
第 3 步:安裝 hardhat 和其他依賴項:
- 在終端中粘貼下面的代碼安裝 hardhat
npm install --save-dev hardhat
- 粘貼下面的代碼以安裝其他依賴項
npm install dotenv @kaiachain/contracts
注意:這將安裝本項目所需的其他依賴項,包括
hardhat
、klaytn/contract
、dotenv
等。
第 4 步:初始化硬頭盔項目:
運行以下命令啟動硬頭盔項目
npx hardhat
在本指南中,您將選擇一個排版腳本項目,如下所示:
注意:初始化項目時,系統會提示安裝
hardhat-toolbox
插件。 該插件捆綁了所有常用軟件包和 Hardhat 插件,建議在開始使用 Hardhat 進行開發時使用。
初始化硬帽項目後,當前目錄應包括
contracts/ - 此文件夾包含智能合約代碼。
ignition/modules/ - 該文件夾包含在區塊鏈網絡上部署合約的代碼。
test/ - 該文件夾包含測試智能合約的所有單元測試。
hardhat.config.js - 該文件包含對 Hardhat 工作和部署靈魂綁定令牌非常重要的配置。
第 5 步創建 .env 文件
現在,在項目文件夾中創建 .env 文件。 該文件可幫助我們將 .env 文件中的環境變量加載到 process.env 文件中。
- 在終端中粘貼此命令以創建 .env 文件
touch .env
- 創建文件後,讓我們將 .env 文件配置為如下所示:
KAIROS_TESTNET_URL= "您的 Kairos RPC 鏈接" PRIVATE_KEY= "從 MetaMask 錢包複製的您的私人密鑰"
注:你也可以選擇使用 hardhat 提供的配置變量 功能來配置不應包含在代碼庫中的變量。
第 6 步:設置Hardhat配置
用以下配置修改 hardhat.config.js
:
require("@nomicfoundation/hardhat-toolbox");require('dotenv').config()module.exports = { solidity: "0.8.17", networks: { kairos: { url: process.env.KAIROS_TESTNET_URL || "", gasPrice: 250000000000, accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [], } }};
現在,我們的開發環境已經準備就緒,讓我們開始編寫我們的靈魂綁定令牌智能合約吧。
創建 SBT 智能合約
在本節中,您將使用 Kaia Contracts:這是一個建立在社區驗證代碼堅實基礎上的安全智能合約開發庫。 它是開放式齊柏林合同的分叉。
注意:您已在 "設置開發環境 "一節的第 3** 步安裝了該庫。
步驟 1:在資源管理器窗格中選擇合同文件夾 ,單擊 "新建文件 "按鈕並創建名為 "SBT.sol "的新文件
第 2步打開文件並粘貼以下代碼:
// SPDX-License-Identifier: MITpragma solidity ^0.8.7;import "@kaiachain/contracts/KIP/token/KIP17/KIP17.sol";import "@kaiachain/contracts/utils/Counters.sol";import "@kaiachain/contracts/access/Ownable.sol";contract SoulBoundToken is KIP17, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIdCounter; constructor() KIP17("SoulBoundToken", "SBT") {} function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } function _beforeTokenTransfer(address from, address to, uint256) pure override internal { require(from == address(0) || to == address(0), "This a Soulbound token. It cannot be transferred."); } function _burn(uint256 tokenId) internal override(KIP17) { super._burn(tokenId); }}
代碼演練
這就是你的智能合約。 第 1 行顯示 Hardhat 使用的是 0.8.7 或更高版本的 Solidity。 除此之外,它還導入了 KIP17.sol 和其他輔助合同。 從第6-12**行開始,創建了一個繼承KIP17的智能合約。 此外,構造函數中還傳遞了標記名稱和符號。
如上代碼所示,令牌名稱和符號已分別設置為 SoulBoundToken 和 SBT。 您可以隨意更改令牌名稱和符號。
該合約的一個主要特點是禁止代幣轉讓,這使得發行的代幣成為靈魂債券。
測試 SBT 智能合約
在本節中,我們將測試一些合同功能。
步驟 1:在資源管理器窗格中,選擇測試文件夾並單擊 "新建文件 "按鈕,創建一個名為 "sbtTest.js "的新文件。
步驟 2:在 sbtTest.js
文件中複製以下代碼。
// This is an example test file. Hardhat will run every *.js file in `test/`,// so feel free to add new ones.// Hardhat tests are normally written with Mocha and Chai.// We import Chai to use its asserting functions here.const { expect } = require("chai");// We use `loadFixture` to share common setups (or fixtures) between tests.// Using this simplifies your tests and makes them run faster, by taking// advantage of Hardhat Network's snapshot functionality.const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers");// `describe` is a Mocha function that allows you to organize your tests.// Having your tests organized makes debugging them easier. All Mocha// functions are available in the global scope.//// `describe` receives the name of a section of your test suite, and a// callback. The callback must define the tests of that section. This callback// can't be an async function.describe("Token contract", function () { // We define a fixture to reuse the same setup in every test. We use // loadFixture to run this setup once, snapshot that state, and reset Hardhat // Network to that snapshot in every test. async function deployTokenFixture() { // Get the ContractFactory and Signers here. const [owner, addr1, addr2] = await ethers.getSigners(); // To deploy our contract, we just have to call ethers.deployContract() and call the // waitForDeployment() method, which happens onces its transaction has been // mined. const sbtContract = await ethers.deployContract("SoulBoundToken"); await sbtContract.waitForDeployment(); // Fixtures can return anything you consider useful for your tests return { sbtContract, owner, addr1, addr2 }; } // You can nest describe calls to create subsections. describe("Deployment", function () { // `it` is another Mocha function. This is the one you use to define each // of your tests. It receives the test name, and a callback function. // // If the callback function is async, Mocha will `await` it. it("Should mint SBT to owner", async function () { const { sbtContract, owner } = await loadFixture(deployTokenFixture); const safemint = await sbtContract.safeMint(owner.address); expect(await sbtContract.ownerOf(0)).to.equal(owner.address); }); }); describe("Transactions", function () { it("Should prohibit token transfer using transferFrom", async function () { const { sbtContract, owner, addr1 } = await loadFixture( deployTokenFixture ); const safemintTx = await sbtContract.safeMint(owner.address); // prohibit token transfer of token id (0) from owner to addr1 await expect( sbtContract.transferFrom(owner.address, addr1.address, 0) ).to.be.reverted; }); it("Should prohibit token transfer using safeTransferFrom", async function () { const { sbtContract, owner, addr1 } = await loadFixture( deployTokenFixture ); const safemintTx = await sbtContract.safeMint(owner.address); // prohibit token transfer of token id (0) from owner to addr1 await expect(sbtContract['safeTransferFrom(address,address,uint256)']( owner.address, addr1.address, 0 )).to.be.reverted; }); });})
在你剛剛複製的代碼中,第 7 行和第 12 行顯示你從 hardhat-network-helpers 的 Chai 和 loadFixture 中導入了 expect。
上述測試可檢查以下內容:
- 特定代幣 ID 的所有者是否與該代幣的鑄造者相同?
- 是否禁止在賬戶之間轉移代幣?
第 3 步:要運行測試,請運行以下命令:
npx 硬帽測試 test/sbtTest.js
如需更深入的測試指南,請查看 Hardhat 測試。
部署智能合約
腳本是 JavaScript/Typescript 文件,可幫助您將合約部署到區塊鏈網絡。 在本節中,您將為智能合約創建一個腳本。
步驟 1:在資源管理器窗格中,選擇 "scripts "文件夾,然後單擊 "新建文件 "按鈕,創建一個名為 "sbtDeploy.js "的新文件。
第 2步將以下代碼複製並粘貼到文件中。
注意:在
deployerAddr
變量中輸入您的 MetaMask 錢包地址。
const { ethers } = require("hardhat");async function main() { const deployerAddr = "Your Metamask wallet address"; const deployer = await ethers.getSigner(deployerAddr); console.log(`Deploying contracts with the account: ${deployer.address}`); console.log(`Account balance: ${(await deployer.provider.getBalance(deployerAddr)).toString()}`); const sbtContract = await ethers.deployContract("SoulBoundToken"); await sbtContract.waitForDeployment();console.log(`Congratulations! You have just successfully deployed your soul bound tokens.`);console.log(`SBT contract address is ${sbtContract.target}. You can verify on https://kairos.kaiascope.com/account/${sbtContract.target}`);}// We recommend this pattern to be able to use async/await everywhere// and properly handle errors.main().catch((error) => { console.error(error); process.exitCode = 1;});
**第 3 步在終端運行以下命令,讓 Hardhat 在 Kaia 測試網絡 (Kairos) 上部署 SBT 令牌
npx hardhat run ignition/modules/sbtDeploy.js --network kairos
第 4 步:打開 Kaiascope,檢查 SBT 令牌是否已成功部署。
第 5 步:在搜索欄中複製並粘貼部署的合同地址,然後按 Enter 鍵。 您應該能看到最近部署的合同。
硬帽叉
Hardhat 為開發人員提供了在本地開發網絡中模擬主網(任何給定區塊)的功能。 這一功能的主要好處之一是,它能讓開發人員與已部署的合同進行交互,還能為複雜的案例編寫測試。
要使該功能有效運行,您需要連接到存檔節點。 您可在 此處 閱讀有關此功能的更多信息。
分叉主網
現在,我們已經建立了 Hardhat 項目,讓我們使用 Hardhat fork Kaia 主網。 打開終端,運行以下命令
npx hardhat node --fork<YOUR ARCHIVE NODE URL>npx hardhat node --fork https://archive-en.node.kaia.io
您也可以配置 hardhat.config.js
- Hardhat Network 始終這樣做:
networks: { hardhat: { forking: { url: "<YOUR ARCHIVE NODE URL>", } }}
輸出
成功運行該命令後,您的終端看起來就像上圖一樣。 您將擁有 20 個開發賬戶,這些賬戶預存了 10,000 個測試代幣。
分叉鏈的 RPC 服務器正在http://127.0.0.1:8545/
監聽。 您可以通過查詢最新的區塊編號來驗證分叉網絡。 讓我們嘗試使用 cURL 訪問 RPC,以獲取區塊編號。 打開一個新的終端窗口,使用以下命令:
curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545
輸出
輸出結果為十六進制,如上圖所示。 要從十六進制中獲取塊編號,請使用此 工具 將十六進制轉換為十進制。 您應該從分叉網絡時獲得最新的區塊編號。 您可以通過 kaiascope確認區塊編號。
在街區分叉
使用硬頭盔,您可以在特定區塊分叉主網。 在這種情況下,讓我們在區塊編號 "105701850 "處分叉鏈。
npx hardhat node --fork<YOUR ARCHIVE NODE URL> --fork-block-number 105701850npx hardhat node --fork https://archive-en.node.kaia.io --fork-block-number 105701850
要在指定區塊確認分叉鏈,請打開一個新的終端窗口並使用以下命令:
curl --data '{"method":"eth_blockNumber","params":[],"id":1,"jsonrpc":"2.0"}' -H "Content-Type: application/json" -X POST localhost:8545
輸出返回十六進制,使用此 工具 轉換後應等於 105701850
。
有關 Hardhat 的更深入指南,請參閱 Hardhat 文檔。 此外,您還可以在 GitHub 上找到本指南的完整代碼實現。