学习 OpenZeppelin 是进入专业 Solidity 开发的重要一步。它不仅能帮你快速构建符合标准的安全合约,还能让你深入理解以太坊生态的最佳实践。
1. 安装 OpenZeppelin Contracts
2.1 安装核心库
在项目根目录运行:
bash
npm install @openzeppelin/contracts
或使用 yarn:
bash
yarn add @openzeppelin/contracts
说明:
- 该包包含所有标准合约(ERC20, ERC721, Ownable 等),不可升级(合约部署后不能修改逻辑)。
- 版本应与你的 Solidity 编译器兼容(例如 Solidity 0.8.x 对应 contracts v4.x)。
2.2 安装可升级合约(可选)
如果你需要部署可升级的合约,安装:
bash
npm install @openzeppelin/contracts-upgradeable
以及 Hardhat 插件:
bash
npm install @openzeppelin/hardhat-upgrades
然后在 hardhat.config.js 中引入:
js
require('@openzeppelin/hardhat-upgrades');
2. 在合约中使用 OpenZeppelin
2.1 导入合约
在 Solidity 文件中通过 import 引入所需合约。
例如创建一个标准的 ERC20 代币:
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") {
// 初始铸币给合约部署者
_mint(msg.sender, 1000000 * 10 ** decimals());
}
// 只有 owner 可以铸币
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
2.2 关键点
- 继承:直接继承 OpenZeppelin 的合约,即可拥有其全部功能。
- 构造函数 :必须调用父合约构造函数(如
ERC20(name, symbol))。 - 修饰器 :如
onlyOwner可直接使用。 - 内部函数 :
_mint,_burn,_transfer等可在子合约中调用。
3. 编译与部署
3.1 使用 Hardhat 编译
bash
npx hardhat compile
3.2 编写部署脚本(Hardhat 示例)
js
// scripts/deploy.js
const hre = require("hardhat");
async function main() {
const MyToken = await hre.ethers.getContractFactory("MyToken");
const token = await MyToken.deploy();
await token.deployed();
console.log("MyToken deployed to:", token.address);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
运行部署:
bash
npx hardhat run scripts/deploy.js --network <network-name>
3.3 可升级合约部署示例
js
const { ethers, upgrades } = require("hardhat");
async function main() {
const MyToken = await ethers.getContractFactory("MyToken");
const proxy = await upgrades.deployProxy(MyToken, []);
await proxy.deployed();
console.log("Proxy deployed to:", proxy.address);
}
测试示例(使用 test-helpers)
js
const { expect } = require("chai");
const { ethers } = require("hardhat");
const { time, expectEvent } = require("@openzeppelin/test-helpers");
describe("MyToken", function () {
it("should mint initial supply to deployer", async function () {
const [owner] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyToken");
const token = await MyToken.deploy();
const ownerBalance = await token.balanceOf(owner.address);
expect(ownerBalance).to.equal(ethers.utils.parseUnits("1000000", 18));
});
});