【Hardhat】編譯&測試&部署智能合約 區塊鏈環境配置
Hardhat 是一個開源的工具箱,用於支持 Ethereum 智能合約開發工作流程。它提供了一組用於編譯、測試和部署智能合約的工具。 Hardhat 可以與 Solidity 和 Vyper 編譯器配合使用,並支持使用 Truffle 和 Buidler 框架的測試。它還提供了一個環境,可以在本地部署和測試智能合約,以及與網絡通信。
要使用 Hardhat 部署智能合約,首先需要安裝 hardhat 並建立專案,然後使用 hardhat compile 命令編譯智能合約,最後使用 hardhat run 部署智能合約到網絡。
文章目錄
- 安裝 Hardhat
- 創建 Hardhat 專案
- 創建 ERC20 合約
- 區塊鏈環境配置
- 測試合約
- 編譯與部署智能合約
- 編譯與部署 Proxy 智能合約
1.安裝 Hardhat
md HardhatDemo
cd HardhatDemo
npm init -yes
npm i hardhat
2.創建 Hardhat 專案
npx hardhat
選擇專案類型
創建完成
安裝套件
npm install --save-dev "hardhat@^2.16.1" "@nomicfoundation/hardhat-toolbox@^3.0.0"
3.創建 ERC20 合約
contracts/IERC20.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
function approve(address spender, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
// ======================================================
// OPTIONAL
// ======================================================
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
contracts/ERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "./IERC20.sol";
contract ERC20 is IERC20 {
//constant
uint8 constant public decimals = 18;
//attribute
string public name;
string public symbol;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
constructor(string memory _name, string memory _symbol, uint256 _totalSupply) {
name = _name;
symbol = _symbol;
_mint(msg.sender, _totalSupply * 1 ether);
}
function transfer(address to, uint256 amount) external virtual returns (bool success) {
_transfer(msg.sender, to, amount);
return true;
}
function approve(address spender, uint256 amount) external virtual returns (bool success) {
_approve(msg.sender, spender, amount);
return true;
}
function transferFrom(address sender, address recipient, uint256 amount) external virtual returns (bool success) {
uint currentAllowance = allowance[sender][msg.sender];
require(currentAllowance >= amount, "ERC20: insufficient allownace");
_approve(sender, msg.sender, currentAllowance - amount);
_transfer(sender, recipient, amount);
return true;
}
function _transfer(address sender, address recipient, uint256 amount) internal {
require(sender != address(0), "ERC20: transfer sender the zero address");
require(recipient != address(0), "ERC20: transfer recipient the zero address");
require(balanceOf[sender] >= amount, "ERC20: transfer amount exceeds balance");
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
}
function _approve(address owner, address spender, uint256 amount) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
allowance[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
balanceOf[account] += amount;
totalSupply += amount;
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
require(balanceOf[account] >= amount, "ERC20: burn amount exceeds balance");
balanceOf[account] -= amount;
totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
}
4.區塊鏈環境配置
hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");
//Proxy才需要
require('@openzeppelin/hardhat-upgrades');
const PRIVATE_KEY = "錢包私鑰";
module.exports = {
solidity: {
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 200
}
}
},
networks: {
bsc: {
url: "https://rpc.ankr.com/bsc",
accounts: [PRIVATE_KEY]
},
bscTestnet: {
url: "https://data-seed-prebsc-2-s3.binance.org:8545",
accounts: [PRIVATE_KEY]
}
}
};
5.測試合約
npm i chai
test/ERC20.js
const {expect} = require('chai');
const {ethers} = require('hardhat');
describe("ERC20 Test", () => {
it("Deploy Contract", async () => {
const [owner, addr1, addr2] = await ethers.getSigners();
const Token = await ethers.getContractFactory("ERC20");
const token = await Token.deploy("USDT", "USDT", 50000000);
const ContractAddress = await token.getAddress();
expect(ContractAddress).to.properAddress;
});
})
npx hardhat test
6.編譯與部署智能合約
scripts/deploy.js
const hre = require("hardhat");
async function main() {
const Contract = await hre.ethers.getContractFactory("ERC20");
const token = await Contract.deploy("USDT", "USDT", 50000000);
await token.waitForDeployment();
console.log(`deployed to: ${await token.getAddress()}`);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
npx hardhat run --network bscTestnet scripts/deploy.js
BSC Testnet USDT
7.編譯與部署 Proxy 智能合約
安裝套件
npm i @openzeppelin/hardhat-upgrades
npm i @openzeppelin/contracts-upgradeable
contracts/GameV1.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract GameV1 is Initializable, OwnableUpgradeable, UUPSUpgradeable {
string public name;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation) internal onlyOwner override {
}
function setName(string memory _name) external {
name = _name;
}
}
scripts/deploy.js
const {ethers, upgrades} = require("hardhat");
async function main() {
const GameV1 = await ethers.getContractFactory("GameV1")
const proxy = await upgrades.deployProxy(GameV1, {initializer: 'initialize', kind: 'uups'})
await proxy.waitForDeployment();
const address = await proxy.getAddress()
console.log("Proxy Contract Address", address)
console.log("Logic Contract Address", await upgrades.erc1967.getImplementationAddress(address))
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
npx hardhat run --network bscTestnet scripts/deploy.js
Proxy Contract
GameV1 Contract
升級合約
contracts/GameV2.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract GameV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable {
string public name;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation) internal onlyOwner override {
}
function setName(string memory _name) external {
name = string(abi.encodePacked("Hi ",_name));
}
}
scripts/deploy.js
const {ethers, upgrades} = require("hardhat");
async function main() {
const proxyAddress = "0xc9A840Cc26AF7e7F6eE3CC6FCF72Df58B7E08c24"
const GameV2 = await ethers.getContractFactory("GameV2")
const proxy = await upgrades.upgradeProxy(proxyAddress, GameV2)
await proxy.waitForDeployment()
const address = await proxy.getAddress()
console.log("Proxy Contract Address", address)
console.log("Logic Contract Address", await upgrades.erc1967.getImplementationAddress(address))
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
npx hardhat run --network bscTestnet scripts/deploy.js
BSC Testnet Proxy
BSC Testnet GameV1
BSC Testnet GameV2