智能合約開發(fā)、測(cè)試祟身、部署全流程(實(shí)操篇)

前言

本文主要對(duì)hardhat框架實(shí)操的介紹奥务,通過一個(gè)簡(jiǎn)單的智能合約案例,用handhat把開發(fā)袜硫、測(cè)試氯葬、部署全流程過一遍。

前期準(zhǔn)備

  • 構(gòu)建工具:hardhat
  • 前端技術(shù)棧:React+Ethersjs+Web3UI Kit
  • 錢包:MetaMask
  • 合約層:Solidity
  • ehterscan區(qū)塊鏈瀏覽器

開始

項(xiàng)目構(gòu)建

# 合約部分
# 創(chuàng)建空文件夾
 mkdir Web3
# 進(jìn)入工程目錄中
cd Web3
# 項(xiàng)目初始化
npm init
# 安裝hardhat
npm install --save-dev hardhat
# hardhat框架初始化選擇項(xiàng)目模板
npx hardhat init
# 下載項(xiàng)目插件
npm install --save-dev @nomicfoundation/hardhat-toolbox
# 運(yùn)行校驗(yàn)
# 在工程目錄下創(chuàng)建文件前端工程目錄
# <<<<<<<<<<<<<<<<<<<<<<<<<
# 前端部分
# 創(chuàng)建一個(gè)dapp項(xiàng)目用于編寫前端程序
 create-react-app dapp
# 進(jìn)入dapp文件夾
cd dapp
# 安裝相應(yīng)的包
# ethers與合約交互
# @web3uikit/core @web3uikit/web3 @web3uikit/icons 錢包相關(guān)包
npm install  xxx

項(xiàng)目目錄結(jié)構(gòu)介紹

web3
   |_contracts//智能合約目錄
   |_test//合約測(cè)試目錄
   |_scripts//合約部署目錄
   |_artifacts//編譯合約自動(dòng)生成
   |___build-info
   |___contracts//前端調(diào)用合約生成的json文件
   |_ignition//
   |_cache//
   |_deploy//部署合約文件目錄
   |_tasts//自定任務(wù)簡(jiǎn)化流程
   |_front_end//前端項(xiàng)目工程目錄
   |_hardhat.config.js//hardhat配置文件
   |_helper-hardhat-config.js//hardhat集中常用變量匯總文件
   |_package.json//工程目錄

Hardhat常用指令

# 獲取所有指令
npx hardhat
# 獲取區(qū)塊網(wǎng)絡(luò)節(jié)點(diǎn)
npx hardhat node
# 編譯合約
npx hardhat compile
# 清除緩存并刪除所有工件
npx hardhat clean
# 測(cè)試合約
npx hardhat test
# 部署合約
npx hardhat run scripts/xx.js 
# 控制臺(tái)
npx hardhat console
# 查看所有指令
npx hardhat 

合約開發(fā)

// SPDX-License-Identifier: MIT
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.22;
import {ERC20} from  "@openzeppelin/contracts/token/ERC20/ERC20.sol"
import {ERC20Burnable} from  "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 
import {ERC20Permit} from  "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol"; 
import {Ownable} from  "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, ERC20Burnable, Ownable, ERC20Permit {
    constructor(address initialOwner)
        ERC20("MyToken", "MTK")
        Ownable(initialOwner)
        ERC20Permit("MyToken")
    {
        _mint(msg.sender, 1000 * 10 ** decimals());
    }
  //鑄造方法
    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}
/**
* 合約說明:
* 使用openzeppelin庫快速構(gòu)建一個(gè)具備鑄造和銷毀的ERC20標(biāo)準(zhǔn)的代幣合約
* 代幣名稱 MyToken,符號(hào) MTK,總量:1000MTK
* 方法:鑄造婉陷,銷毀帚称,轉(zhuǎn)賬,授權(quán)...
*/

合約編譯

# 編譯合約指令
npx hardhat compile

合約測(cè)試

# 合約測(cè)試指令
npx hardhat test

合約測(cè)試樣例

說明:hardhat框架集成了chai測(cè)試的工具

const { ethers,getNamedAccounts,deployments} = require("hardhat");
const {assert,expect } = require("chai");
let firstAccount;
let nft;
// 部署合約
beforeEach(async function () {
  firstAccount = (await getNamedAccounts()).firstAccount;
  await deployments.fixture(["all"]);
  nft=await ethers.getContract("MyNFT",firstAccount);
});
// 測(cè)試單元 參數(shù)說明:測(cè)試單元命名
describe("xxx", async () => {
  it("mint nft", async function () {
    await nft.safeMint(firstAccount);
    let owner=await nft.ownerOf(0);
    expect(owner).to.equal(firstAccount);
  });
//.........
});

合約部署指令

# 部署合約
# 說明:部署一個(gè)在scripts目錄下的deploy.js 網(wǎng)絡(luò)節(jié)點(diǎn)
# network-name可以在hardhat.json中配置網(wǎng)絡(luò):localhost秽澳,
npx hardhat run scripts/xxx.js --network <network-name>
# 例如
npx hardhat run scripts/deploy.js 
 or 
npx hardhat run scripts/deploy.js --network localhost//指定網(wǎng)絡(luò)

合約部署樣例1

# 直接在scripts目錄下編寫部署腳本
const { ethers } = require("hardhat");

async function main() {
    const [deployer] = await ethers.getSigners();//獲取網(wǎng)絡(luò)節(jié)點(diǎn)賬號(hào)
    // console.log(deployer)
    console.log("Deploying contracts with the account:", deployer.address);
    const MyToken = await ethers.getContractFactory("MyToken");//獲取合約
    console.log(MyToken)//獲取合約實(shí)例
    const myToken = await MyToken.deploy();//部署合約
    console.log("Token address:", myToken.target);//打印合約地址

}
main().then(()=>{
    process.exit(0)
}).catch((err)=>{
console.error(err)
process.exit(1)
});
# 使用指令
npx hardhat run scripts/xxx.js --network 指定網(wǎng)絡(luò)節(jié)點(diǎn)

合約部署樣例2

# 在deploy目錄下部署腳本(推薦此操作)
# 使用hardhat部署插件hardhat-deploy
#1 在配置文件hardhat.confing.js中引入插件
require("hardhat-deploy");
require("@nomicfoundation/hardhat-ethers");
require("hardhat-deploy-ethers");
# 在deploy目錄下的部署文件:
// module.exports = async (hre) => {
//     const getNamedAccounts = hre.getNamedAccounts
//     const deployments = hre.deployments
//  }
const { network } =require("hardhat");

 module.exports = async ({getNamedAccounts,deployments}) => {
    const firstAccount = (await getNamedAccounts()).firstAccount;//獲取節(jié)點(diǎn)1
    const secondAccount = (await getNamedAccounts()).secondAccount;//獲取節(jié)點(diǎn)2
    const {deploy}= deployments//獲取部署實(shí)例
//部署合約
    const FundMe= await deploy("FundMe",{
        from: firstAccount,//節(jié)點(diǎn)
        args: [xx,xxx],//要傳的參數(shù)xx闯睹,xxx
        log: true,//是否打印log
        //waitConfirmations: confirmations,//等待的區(qū)塊數(shù)
    })
  //驗(yàn)證合約是否成功的部署的鏈上
        await hre.run("verify:verify", {
            address: FundMe.address,
            constructorArguments: [xx,xxx],//合約要傳的參數(shù)xx,xxx
            });
 }
 module.exports.tags = ["all","fundme"];//在執(zhí)行部署指令時(shí)可選參數(shù)
# 例如
npx hardhat deploy --tag fundme or all
# 等同于
npx hardhat run scripts/funfme.js

自定義task樣例

# 說明
# 文件目錄
tasks
  |_deploy_nft.js
  |_index.js
  |_ deploy_tonken.js  
# deploy_nft.js代碼如下
const {task} = require("hardhat/config");//hardhat集成的插件
//參數(shù)說明担神,deploy-nft是指令名
task("deploy-fundme", "deploy-fundme").setAction(async (taskArgs, hre) => {
  const FundMeFactory = await ethers.getContractFactory("MyNFT");//獲取合約
  const fundMe = await FundMeFactory.deploy(300);//部署需要傳遞的參數(shù)
  await fundMe.waitForDeployment();//幾個(gè)區(qū)塊
  console.log(`address : ${fundMe.target}`)
  if(hre.network.config.chainId == 11155111 && process.env.ETHERSCAN_API_KEY){
      console.log("Waiting for 5 confirmations")
      await fundMe.deploymentTransaction().wait(5) //等待5個(gè)區(qū)塊交易完成
      await verifyFundMe(fundMe.target, [300])
  }else{
      console.log("verification skipped..")
  }

});
async function verifyFundMe(factoryAddr,args){
  await hre.run("verify:verify", {
      address: factoryAddr,
      constructorArguments: [50],
      });
}
exports.default = {};
# deploy_token.js代碼同上簡(jiǎn)單修改代碼即可
# index.js如下
exports.deployfundme=require("./deploy_nft.js")
exports.interactfundme=require("./deploy_token.js")
把自定task引入hardhat.config.js中
require(./tasks);
# 可以通過npx hardhat 查看是否生成
# 導(dǎo)入成功后
可以在控制臺(tái)面板中的AVAILABLE TASKS查看到
# deploy-token
npx hardhat deploy-fundme --network xxx  //如果有參數(shù)傳遞就后面跟參數(shù) 鍵名 鍵值
# deploy-nft 
同上修改一下關(guān)鍵參數(shù)即可

hardhat.config.js文件配置

require("@nomicfoundation/hardhat-toolbox");
require("@nomicfoundation/hardhat-verify");//驗(yàn)證合約
// require("@chainlink/env-enc").config();//可以采用此包對(duì).env常量進(jìn)行加密處理
require("dotenv").config();//讀取.env的內(nèi)容
require("./tasks");//自定義task楼吃,作用主要快加高效的執(zhí)行自定指令,便于高效的開發(fā)。
//引入部署插件
require("hardhat-deploy");
require("@nomicfoundation/hardhat-ethers");
require("hardhat-deploy-ethers");
//讀取env的常量
process.env.PRIVATE_KEY = process.env.PRIVATE_KEY;
process.env.PRIVATE_KEY_1 = process.env.PRIVATE_KEY_1;
process.env.ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY;
process.env.ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY;
module.exports = {
  solidity: "0.8.27",
  defaultNetwork: "hardhat",
  mocha: {
    timeout: 300000//設(shè)置mocha的超時(shí)時(shí)間為300秒所刀,解決集成測(cè)試時(shí)間不夠問題
  },
  networks: {
    // localhost: {
    //   url:'http://127.0.0.1:8545/',
    //   chainId: 31337,
    // },
    sepolia: {
      url: `https://sepolia.infura.io/v3/${process.env.ALCHEMY_API_KEY}`,
      accounts: [process.env.PRIVATE_KEY,process.env.PRIVATE_KEY_1],
      chainId: 11155111
    },
  },//網(wǎng)絡(luò)節(jié)點(diǎn)設(shè)置
  etherscan: {
    apiKey: {
      sepolia: process.env.ETHERSCAN_API_KEY
    }
  },//設(shè)置 Etherscan API 密鑰的,用于與 Etherscan 服務(wù)進(jìn)行交互
  sourcify: {
    enabled: true
  },//啟動(dòng) Hardhat 插件 @nomicfoundation/hardhat-verify捞挥,用戶驗(yàn)證部署在以太坊區(qū)塊鏈上的智能合約的源代碼
  namedAccounts: {
    firstAccount: {
      default: 0
    },
    secondAccount: {
      default: 1
    }
  },//通過名字獲取區(qū)塊節(jié)點(diǎn)
  gasReporter: {
    enabled: false,//控制gas報(bào)告的顯示
  }
};

前端調(diào)用合約

# 前端主要通過Ethers.js或者Web3.js和合約進(jìn)行交互浮创。
#關(guān)于Ehers.js庫的使用。
* Provider:提供者
* Signer:簽名者
* Contract:用于合約的獲取
* 其他(單位轉(zhuǎn)換砌函,)
# 前端獲取合約
import myToken  from "../json/MyToken.json";//不是之后生成的json文件
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider();//獲取provider 
let TokenAddress="0x......"http://合約地址
let signer=await provider.signer();//獲取簽名
# 獲取合約 參數(shù):合約地址 合約abi斩披,簽名如果是signer可讀寫,provider可讀
const contractWETH = new ethers.Contract(TokenAddress, myToken.abi, signer);
# 獲取合約后進(jìn)行交互操作

插件篇

# 推薦工具
# hardhat-deploy合約部署相關(guān)的插件
# hardhat-gas-reporter部署合約后會(huì)生成一個(gè)gas費(fèi)的報(bào)告文件

總結(jié)

以上就是使用hardhat構(gòu)建工具讹俊,把編寫的智能合約從開發(fā)垦沉,測(cè)試,部署上鏈的整個(gè)流程仍劈。

  • 使用內(nèi)嵌測(cè)試包c(diǎn)hai厕倍,快速驗(yàn)證合約的可行性;
  • 使用到了hardhat-deploy插件贩疙,實(shí)現(xiàn)快速部署合約讹弯;
  • 使用@nomicfoundation/hardhat-verify,驗(yàn)證合約是否成功上鏈这溅;
  • 以及合約部署gas費(fèi)消耗分析報(bào)告hardhat-gas-reporter组民。便于優(yōu)化合約;
  • 通過自定task悲靴,提高開發(fā)效率臭胜;
  • 開發(fā)中還會(huì)用到預(yù)言機(jī)chainlink相關(guān)的方法
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市癞尚,隨后出現(xiàn)的幾起案子耸三,更是在濱河造成了極大的恐慌,老刑警劉巖浇揩,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吕晌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡临燃,警方通過查閱死者的電腦和手機(jī)睛驳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膜廊,“玉大人乏沸,你說我怎么就攤上這事∽希” “怎么了蹬跃?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)铆铆。 經(jīng)常有香客問我蝶缀,道長(zhǎng)丹喻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任翁都,我火速辦了婚禮碍论,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘柄慰。我一直安慰自己鳍悠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布坐搔。 她就那樣靜靜地躺著藏研,像睡著了一般。 火紅的嫁衣襯著肌膚如雪概行。 梳的紋絲不亂的頭發(fā)上蠢挡,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音凳忙,去河邊找鬼袒哥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛消略,可吹牛的內(nèi)容都是我干的堡称。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼艺演,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼却紧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胎撤,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤晓殊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后伤提,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巫俺,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年肿男,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了介汹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舶沛,死狀恐怖嘹承,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情如庭,我是刑警寧澤叹卷,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響骤竹,放射性物質(zhì)發(fā)生泄漏帝牡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一蒙揣、第九天 我趴在偏房一處隱蔽的房頂上張望靶溜。 院中可真熱鬧,春花似錦鸣奔、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至断楷,卻和暖如春锨匆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背冬筒。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工恐锣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舞痰。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓土榴,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親响牛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子玷禽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容