OpenZeppelin合约技能Skill openzeppelin

OpenZeppelin合约技能是用于安全、高效地开发以太坊及其他EVM兼容区块链智能合约的标准库。它提供了经过审计的、模块化的Solidity合约组件,涵盖访问控制、代币标准、治理、可升级合约和安全工具等关键领域。关键词:智能合约开发,Solidity库,区块链安全,ERC-20,ERC-721,可升级合约,去中心化治理,OpenZeppelin。

智能合约 0 次安装 0 次浏览 更新于 2/23/2026

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 质押

最佳实践

  1. 始终使用最新的稳定版本
  2. 理解继承顺序
  3. 使用向导创建起始模板
  4. 审计自定义扩展
  5. 彻底测试升级路径

另请参阅