# 使用Hardhat部署合约到链上 > Hardhat部署简单合约至链上的基本流程 **Published by:** [Confucian](https://paragraph.com/@confucian/) **Published on:** 2022-11-05 **Categories:** hardhat **URL:** https://paragraph.com/@confucian/hardhat-tutorial ## Content 前言参考 Hardhat tutorial 文档:https://hardhat.org/tutorial 所需环境:Node.jsyarnMetaMask(浏览器插件)预备知识:区块链基础知识智能合约基础知识NPMJavascriptSolidity正文初始化项目创建一个文件夹作为你的项目存放路径(我取名 demo)后面所有命令皆在项目根目录下。 在项目文件夹下初始化 npm 项目:yarn init -y安装 hardhat 依赖yarn add --dev hardhat运行 npx hardhat 并选择 Create an empty hardhat.config.js$ npx hardhat 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 888 888 "88b 888P" d88" 888 888 "88b "88b 888 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 👷 Welcome to Hardhat v2.9.9 👷 ? What do you want to do? … Create a JavaScript project Create a TypeScript project ❯ Create an empty hardhat.config.js Quit这时在你的项目路径下会创建一个 hardhat.config.js 文件:/** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.17", };里面指定了 solidity 编译器版本。 我们还需要安装一些后面要用的插件:yarn add --dev @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomiclabs/hardhat-ethers @nomiclabs/hardhat-etherscan chai ethers hardhat-gas-reporter solidity-coverage @typechain/hardhat typechain @typechain/ethers-v5 @ethersproject/abi @ethersproject/providers编写智能合约创建 contracts 文件夹,并在其路径下创建 Token.sol 智能合约文件。 复制下方代码到 Token.sol 文件里://SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; contract Token { // 代币的名字和简称 string public name = "My Hardhat Token"; string public symbol = "MHT"; // 总供应量 uint256 public totalSupply = 1000000; // 合约所有者 address public owner; // address 到 uint 的映射,用来记录地址的代币余额 mapping(address => uint256) balances; // 代币转账事件 event Transfer(address indexed _from, address indexed _to, uint256 _value); // 部署合约时,为部署地址铸造总供应量代币,并设置为合约所有者 constructor() { balances[msg.sender] = totalSupply; owner = msg.sender; } // 转账函数 function transfer(address to, uint256 amount) external { // 检查调用者余额是否满足转账金额 require(balances[msg.sender] >= amount, "Not enough tokens"); // 转移数量 balances[msg.sender] -= amount; balances[to] += amount; // 触发 Transfer 事件 emit Transfer(msg.sender, to, amount); } // 查看指定账户的余额 function balanceOf(address account) external view returns (uint256) { return balances[account]; } }编译合约可以通过 npx hardhat compile 命令来编译合约。$ npx hardhat compile Compiling 1 file with 0.8.9 Compilation finished successfully编写合约测试合约测试非必需环节,可以跳过不写。创建 test 文件夹,并在其路径下创建 Token.js 文件。 复制下方代码到 Token.js 文件里:const { expect } = require("chai"); const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); describe("Token contract", function () { async function deployTokenFixture() { const Token = await ethers.getContractFactory("Token"); const [owner, addr1, addr2] = await ethers.getSigners(); const hardhatToken = await Token.deploy(); await hardhatToken.deployed(); return { Token, hardhatToken, owner, addr1, addr2 }; } describe("Deployment", function () { it("Should set the right owner", async function () { const { hardhatToken, owner } = await loadFixture(deployTokenFixture); expect(await hardhatToken.owner()).to.equal(owner.address); }); it("Should assign the total supply of tokens to the owner", async function () { const { hardhatToken, owner } = await loadFixture(deployTokenFixture); const ownerBalance = await hardhatToken.balanceOf(owner.address); expect(await hardhatToken.totalSupply()).to.equal(ownerBalance); }); }); describe("Transactions", function () { it("Should transfer tokens between accounts", async function () { const { hardhatToken, owner, addr1, addr2 } = await loadFixture( deployTokenFixture ); await expect( hardhatToken.transfer(addr1.address, 50) ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]); await expect( hardhatToken.connect(addr1).transfer(addr2.address, 50) ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]); }); it("should emit Transfer events", async function () { const { hardhatToken, owner, addr1, addr2 } = await loadFixture( deployTokenFixture ); await expect(hardhatToken.transfer(addr1.address, 50)) .to.emit(hardhatToken, "Transfer") .withArgs(owner.address, addr1.address, 50); await expect(hardhatToken.connect(addr1).transfer(addr2.address, 50)) .to.emit(hardhatToken, "Transfer") .withArgs(addr1.address, addr2.address, 50); }); it("Should fail if sender doesn't have enough tokens", async function () { const { hardhatToken, owner, addr1 } = await loadFixture( deployTokenFixture ); const initialOwnerBalance = await hardhatToken.balanceOf(owner.address); await expect( hardhatToken.connect(addr1).transfer(owner.address, 1) ).to.be.revertedWith("Not enough tokens"); expect(await hardhatToken.balanceOf(owner.address)).to.equal( initialOwnerBalance ); }); }); });测试文件涉及了 ether.js (合约交互库)和一些测试库(chai,hardhat)。测试合约可以运行 npx hardhat test 来对合约进行测试。$ npx hardhat test Token contract Deployment ✓ Should set the right owner ✓ Should assign the total supply of tokens to the owner Transactions ✓ Should transfer tokens between accounts (199ms) ✓ Should fail if sender doesn’t have enough tokens ✓ Should update balances after transfers (111ms) 5 passing (1s)部署合约我们已经完成了合约的编写和测试,接下来将合约部署到链上。编写部署脚本创建 scripts 文件夹,并在其路径下创建 deploy.js 文件。 复制下方代码到 deploy.js 文件里:async function main() { const [deployer] = await ethers.getSigners(); console.log("Deploying contracts with the account:", deployer.address); console.log("Account balance:", (await deployer.getBalance()).toString()); const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy(); console.log("Token address:", token.address); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });部署合约可以运行 npx hardhat run scripts/deploy.js --network 来部署合约到指定的链(network)。 但我们需要先配置下链(网络)信息。配置链(网络)信息由于以太坊Goerli测试网太堵,我们使用Matic链的mumbai测试网修改 hardhat.config.js 文件如下:require("@nomicfoundation/hardhat-toolbox"); // 下面这行写你的开发钱包私钥 const PRIVATE_KEY = "YOUR GOERLI PRIVATE KEY"; module.exports = { solidity: "0.8.9", networks: { mumbai: { // 将你的 API URL 写到下面这行 url: "YOUR API URL", accounts: [PRIVATE_KEY] } } };注意:开发钱包内不要有主网币,避免丢失。获得 API URL我们需要用 API URL 来连接到节点进而发出交易。 有2种主流的 provider 网站: https://infura.io/ https://www.alchemy.com/ 进入对应网页注册好账号后即可获取 API URL部署合约上链配置好 hardhat.config.js 文件后,运行 npx hardhat run scripts/deploy.js --network mumbai 即可部署到链上。具体信息可通过 provider 的 dashboard 查询。结语以上就是使用hardhat部署简单合约上链的基本流程。 运行 npx hardhat 并选择 Create an empty hardhat.config.js$ npx hardhat 888 888 888 888 888 888 888 888 888 888 888 888 888 888 888 8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888 888 888 "88b 888P" d88" 888 888 "88b "88b 888 888 888 .d888888 888 888 888 888 888 .d888888 888 888 888 888 888 888 Y88b 888 888 888 888 888 Y88b. 888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888 👷 Welcome to Hardhat v2.9.9 👷 ? What do you want to do? … Create a JavaScript project Create a TypeScript project ❯ Create an empty hardhat.config.js Quit这时在你的项目路径下会创建一个 hardhat.config.js 文件:/** @type import('hardhat/config').HardhatUserConfig */ module.exports = { solidity: "0.8.17", };里面指定了 solidity 编译器版本。 我们还需要安装一些后面要用的插件:yarn add --dev @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-network-helpers @nomicfoundation/hardhat-chai-matchers @nomiclabs/hardhat-ethers @nomiclabs/hardhat-etherscan chai ethers hardhat-gas-reporter solidity-coverage @typechain/hardhat typechain @typechain/ethers-v5 @ethersproject/abi @ethersproject/providers编写智能合约创建 contracts 文件夹,并在其路径下创建 Token.sol 智能合约文件。 复制下方代码到 Token.sol 文件里://SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.9; contract Token { // 代币的名字和简称 string public name = "My Hardhat Token"; string public symbol = "MHT"; // 总供应量 uint256 public totalSupply = 1000000; // 合约所有者 address public owner; // address 到 uint 的映射,用来记录地址的代币余额 mapping(address => uint256) balances; // 代币转账事件 event Transfer(address indexed _from, address indexed _to, uint256 _value); // 部署合约时,为部署地址铸造总供应量代币,并设置为合约所有者 constructor() { balances[msg.sender] = totalSupply; owner = msg.sender; } // 转账函数 function transfer(address to, uint256 amount) external { // 检查调用者余额是否满足转账金额 require(balances[msg.sender] >= amount, "Not enough tokens"); // 转移数量 balances[msg.sender] -= amount; balances[to] += amount; // 触发 Transfer 事件 emit Transfer(msg.sender, to, amount); } // 查看指定账户的余额 function balanceOf(address account) external view returns (uint256) { return balances[account]; } }编译合约可以通过 npx hardhat compile 命令来编译合约。$ npx hardhat compile Compiling 1 file with 0.8.9 Compilation finished successfully编写合约测试合约测试非必需环节,可以跳过不写。创建 test 文件夹,并在其路径下创建 Token.js 文件。 复制下方代码到 Token.js 文件里:const { expect } = require("chai"); const { loadFixture } = require("@nomicfoundation/hardhat-network-helpers"); describe("Token contract", function () { async function deployTokenFixture() { const Token = await ethers.getContractFactory("Token"); const [owner, addr1, addr2] = await ethers.getSigners(); const hardhatToken = await Token.deploy(); await hardhatToken.deployed(); return { Token, hardhatToken, owner, addr1, addr2 }; } describe("Deployment", function () { it("Should set the right owner", async function () { const { hardhatToken, owner } = await loadFixture(deployTokenFixture); expect(await hardhatToken.owner()).to.equal(owner.address); }); it("Should assign the total supply of tokens to the owner", async function () { const { hardhatToken, owner } = await loadFixture(deployTokenFixture); const ownerBalance = await hardhatToken.balanceOf(owner.address); expect(await hardhatToken.totalSupply()).to.equal(ownerBalance); }); }); describe("Transactions", function () { it("Should transfer tokens between accounts", async function () { const { hardhatToken, owner, addr1, addr2 } = await loadFixture( deployTokenFixture ); await expect( hardhatToken.transfer(addr1.address, 50) ).to.changeTokenBalances(hardhatToken, [owner, addr1], [-50, 50]); await expect( hardhatToken.connect(addr1).transfer(addr2.address, 50) ).to.changeTokenBalances(hardhatToken, [addr1, addr2], [-50, 50]); }); it("should emit Transfer events", async function () { const { hardhatToken, owner, addr1, addr2 } = await loadFixture( deployTokenFixture ); await expect(hardhatToken.transfer(addr1.address, 50)) .to.emit(hardhatToken, "Transfer") .withArgs(owner.address, addr1.address, 50); await expect(hardhatToken.connect(addr1).transfer(addr2.address, 50)) .to.emit(hardhatToken, "Transfer") .withArgs(addr1.address, addr2.address, 50); }); it("Should fail if sender doesn't have enough tokens", async function () { const { hardhatToken, owner, addr1 } = await loadFixture( deployTokenFixture ); const initialOwnerBalance = await hardhatToken.balanceOf(owner.address); await expect( hardhatToken.connect(addr1).transfer(owner.address, 1) ).to.be.revertedWith("Not enough tokens"); expect(await hardhatToken.balanceOf(owner.address)).to.equal( initialOwnerBalance ); }); }); });测试文件涉及了 ether.js (合约交互库)和一些测试库(chai,hardhat)。测试合约可以运行 npx hardhat test 来对合约进行测试。$ npx hardhat test Token contract Deployment ✓ Should set the right owner ✓ Should assign the total supply of tokens to the owner Transactions ✓ Should transfer tokens between accounts (199ms) ✓ Should fail if sender doesn’t have enough tokens ✓ Should update balances after transfers (111ms) 5 passing (1s)部署合约我们已经完成了合约的编写和测试,接下来将合约部署到链上。编写部署脚本创建 scripts 文件夹,并在其路径下创建 deploy.js 文件。 复制下方代码到 deploy.js 文件里:async function main() { const [deployer] = await ethers.getSigners(); console.log("Deploying contracts with the account:", deployer.address); console.log("Account balance:", (await deployer.getBalance()).toString()); const Token = await ethers.getContractFactory("Token"); const token = await Token.deploy(); console.log("Token address:", token.address); } main() .then(() => process.exit(0)) .catch((error) => { console.error(error); process.exit(1); });部署合约可以运行 npx hardhat run scripts/deploy.js --network 来部署合约到指定的链(network)。 但我们需要先配置下链(网络)信息。配置链(网络)信息由于以太坊Goerli测试网太堵,我们使用Matic链的mumbai测试网修改 hardhat.config.js 文件如下:require("@nomicfoundation/hardhat-toolbox"); // 下面这行写你的开发钱包私钥 const PRIVATE_KEY = "YOUR GOERLI PRIVATE KEY"; module.exports = { solidity: "0.8.9", networks: { mumbai: { // 将你的 API URL 写到下面这行 url: "YOUR API URL", accounts: [PRIVATE_KEY] } } };注意:开发钱包内不要有主网币,避免丢失。获得 API URL我们需要用 API URL 来连接到节点进而发出交易。 有2种主流的 provider 网站: https://infura.io/ https://www.alchemy.com/ 进入对应网页注册好账号后即可获取 API URL部署合约上链配置好 hardhat.config.js 文件后,运行 npx hardhat run scripts/deploy.js --network mumbai 即可部署到链上。具体信息可通过 provider 的 dashboard 查询。结语以上就是使用hardhat部署简单合约上链的基本流程。TODO“编写合约测试”-代码文件写注释。 ## Publication Information - [Confucian](https://paragraph.com/@confucian/): Publication homepage - [All Posts](https://paragraph.com/@confucian/): More posts from this publication - [RSS Feed](https://api.paragraph.com/blogs/rss/@confucian): Subscribe to updates - [Twitter](https://twitter.com/Confucian_e): Follow on Twitter ## Optional - [Collect as NFT](https://paragraph.com/@confucian/hardhat-tutorial): Support the author by collecting this post - [View Collectors](https://paragraph.com/@confucian/hardhat-tutorial/collectors): See who has collected this post