name: openzeppelin description: 精通使用OpenZeppelin Contracts库进行安全的智能合约开发。涵盖访问控制、代币标准、治理、升级和安全工具。 allowed-tools: Read, Grep, Write, Bash, Edit, Glob, WebFetch
OpenZeppelin Contracts 技能
精通使用OpenZeppelin Contracts,这是用于安全智能合约开发的标准库。
能力
- 访问控制:Ownable、AccessControl、Governor模式
- 可升级合约:UUPS和透明代理插件
- 代币标准:ERC-20、ERC-721、ERC-1155实现
- 安全工具:ReentrancyGuard、Pausable、SafeERC20
- 治理:Governor和TimelockController
- 元交易:ERC-2771支持
- 加密工具:ECDSA、MerkleProof、EIP712
安装
# 标准合约
npm install @openzeppelin/contracts
# 可升级合约
npm install @openzeppelin/contracts-upgradeable
# Hardhat升级插件
npm install @openzeppelin/hardhat-upgrades
访问控制
Ownable
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyContract is Ownable {
constructor(address initialOwner) Ownable(initialOwner) {}
function protectedFunction() external onlyOwner {
// 只有所有者可以调用
}
}
AccessControl(基于角色)
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyContract is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}
function mint(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
// 铸造逻辑
}
function pause() external onlyRole(PAUSER_ROLE) {
// 暂停逻辑
}
}
AccessControlEnumerable
import "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
contract MyContract is AccessControlEnumerable {
function getAllAdmins() external view returns (address[] memory) {
uint256 count = getRoleMemberCount(DEFAULT_ADMIN_ROLE);
address[] memory admins = new address[](count);
for (uint256 i = 0; i < count; i++) {
admins[i] = getRoleMember(DEFAULT_ADMIN_ROLE, i);
}
return admins;
}
}
代币标准
带扩展的ERC-20
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract MyToken is ERC20, ERC20Burnable, ERC20Pausable, ERC20Permit, ERC20Votes, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
constructor() ERC20("MyToken", "MTK") ERC20Permit("MyToken") {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MINTER_ROLE, msg.sender);
_grantRole(PAUSER_ROLE, msg.sender);
}
function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
_mint(to, amount);
}
function pause() public onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() public onlyRole(PAUSER_ROLE) {
_unpause();
}
// 必需的重写函数
function _update(address from, address to, uint256 value)
internal
override(ERC20, ERC20Pausable, ERC20Votes)
{
super._update(from, to, value);
}
function nonces(address owner)
public
view
override(ERC20Permit, Nonces)
returns (uint256)
{
return super.nonces(owner);
}
}
带扩展的ERC-721
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Royalty.sol";
contract MyNFT is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Royalty {
uint256 private _tokenIdCounter;
constructor() ERC721("MyNFT", "MNFT") {
_setDefaultRoyalty(msg.sender, 500); // 5%版税
}
function safeMint(address to, string memory uri) public {
uint256 tokenId = _tokenIdCounter++;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
// 必需的重写函数
function _update(address to, uint256 tokenId, address auth)
internal
override(ERC721, ERC721Enumerable)
returns (address)
{
return super._update(to, tokenId, auth);
}
function _increaseBalance(address account, uint128 value)
internal
override(ERC721, ERC721Enumerable)
{
super._increaseBalance(account, value);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Royalty)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
治理
Governor设置
import "@openzeppelin/contracts/governance/Governor.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol";
import "@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol";
contract MyGovernor is
Governor,
GovernorSettings,
GovernorCountingSimple,
GovernorVotes,
GovernorVotesQuorumFraction,
GovernorTimelockControl
{
constructor(
IVotes _token,
TimelockController _timelock
)
Governor("MyGovernor")
GovernorSettings(
1 days, // 投票延迟
1 weeks, // 投票周期
100e18 // 提案门槛
)
GovernorVotes(_token)
GovernorVotesQuorumFraction(4) // 4%法定人数
GovernorTimelockControl(_timelock)
{}
// 必需的重写函数...
}
可升级合约
UUPS模式
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract MyContractV1 is Initializable, UUPSUpgradeable, OwnableUpgradeable {
uint256 public value;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() public initializer {
__Ownable_init(msg.sender);
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
override
onlyOwner
{}
}
部署脚本
const { ethers, upgrades } = require("hardhat");
async function main() {
// 部署
const MyContract = await ethers.getContractFactory("MyContractV1");
const proxy = await upgrades.deployProxy(MyContract, [], {
initializer: "initialize",
kind: "uups",
});
await proxy.waitForDeployment();
console.log("代理地址:", await proxy.getAddress());
// 升级
const MyContractV2 = await ethers.getContractFactory("MyContractV2");
await upgrades.upgradeProxy(await proxy.getAddress(), MyContractV2);
}
安全工具
SafeERC20
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract MyContract {
using SafeERC20 for IERC20;
function deposit(IERC20 token, uint256 amount) external {
token.safeTransferFrom(msg.sender, address(this), amount);
}
function withdraw(IERC20 token, uint256 amount) external {
token.safeTransfer(msg.sender, amount);
}
}
ReentrancyGuard
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
contract MyContract is ReentrancyGuard {
function withdraw() external nonReentrant {
// 防止重入攻击
}
}
MerkleProof
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
contract Airdrop {
bytes32 public merkleRoot;
function claim(bytes32[] calldata proof, uint256 amount) external {
bytes32 leaf = keccak256(abi.encodePacked(msg.sender, amount));
require(MerkleProof.verify(proof, merkleRoot, leaf), "无效证明");
// 处理申领
}
}
流程集成
| 流程 | 目的 |
|---|---|
| 所有代币流程 | 代币开发 |
governance-system.js |
治理 |
smart-contract-upgrade.js |
升级 |
staking-contract.js |
质押 |
最佳实践
- 始终使用最新的稳定版本
- 理解继承顺序
- 使用向导创建起始模板
- 审计自定义扩展
- 彻底测试升级路径
另请参阅
skills/solidity-dev/SKILL.md- Solidity开发- OpenZeppelin文档
- 合约向导