develop-secure-contracts

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Develop Secure Smart Contracts with OpenZeppelin

使用OpenZeppelin开发安全智能合约

Core Workflow

核心工作流

Understand the Request Before Responding

响应前先理解需求

For conceptual questions ("How does Ownable work?"), explain without generating code. For implementation requests, proceed with the workflow below.
对于概念性问题(如“Ownable如何工作?”),仅做解释,不生成代码。对于实现类请求,按照以下工作流操作。

CRITICAL: Always Read the Project First

关键要求:务必先了解项目情况

Before generating code or suggesting changes:
  1. Search the user's project for existing contracts (
    Glob
    for
    **/*.sol
    ,
    **/*.cairo
    ,
    **/*.rs
    , etc.)
  2. Read the relevant contract files to understand what already exists
  3. Default to integration, not replacement — when users say "add pausability" or "make it upgradeable", they mean modify their existing code, not generate something new. Only replace if explicitly requested ("start fresh", "replace this").
If a file cannot be read, surface the failure explicitly — report the path attempted and the reason. Ask whether the path is correct. Never silently fall back to a generic response as if the file does not exist.
在生成代码或建议修改之前:
  1. 搜索用户项目中的现有合约(使用
    Glob
    匹配
    **/*.sol
    **/*.cairo
    **/*.rs
    等文件)
  2. 阅读相关合约文件,了解已有的实现
  3. 优先集成,而非替换——当用户说“添加 pausability”或“使其可升级”时,指的是修改现有代码,而非生成新代码。只有当用户明确要求(如“重新开始”、“替换这个”)时才进行替换。
如果无法读取文件,需明确告知失败情况——说明尝试的路径和原因,询问路径是否正确。绝不能默认文件不存在而给出通用回复。

Fundamental Rule: Prefer Library Components Over Custom Code

基本原则:优先使用库组件,而非自定义代码

Before writing ANY logic, search the OpenZeppelin library for an existing component:
  1. Exact match exists? Import and use it directly — inherit, implement its trait, compose with it. Done.
  2. Close match exists? Import and extend it — override only functions the library marks as overridable (virtual, hooks, configurable parameters).
  3. No match exists? Only then write custom logic. Confirm by browsing the library's directory structure first.
NEVER copy or embed library source code into the user's contract. Always import from the dependency so the project receives security updates. Never hand-write what the library already provides:
  • Never write a custom
    paused
    modifier when
    Pausable
    or
    ERC20Pausable
    exists
  • Never write
    require(msg.sender == owner)
    when
    Ownable
    exists
  • Never implement ERC165 logic when the library's base contracts already handle it
在编写任何逻辑之前,先在OpenZeppelin库中查找现有组件:
  1. 存在完全匹配的组件? 直接导入并使用——通过继承、实现 trait 或组合的方式完成。操作结束。
  2. 存在近似匹配的组件? 导入并扩展它——仅重写库中标注为可重写的函数(virtual、钩子函数、可配置参数)。
  3. 没有匹配的组件? 仅在此时编写自定义逻辑。编写前需先浏览库的目录结构确认。
绝不要将库的源代码复制或嵌入到用户的合约中。 始终从依赖中导入,以便项目能接收安全更新。绝不要手写库已提供的功能:
  • Pausable
    ERC20Pausable
    存在时,绝不要自定义
    paused
    修饰器
  • Ownable
    存在时,绝不要手写
    require(msg.sender == owner)
  • 当库的基础合约已处理ERC165逻辑时,绝不要自行实现

Methodology

方法论

The primary workflow is pattern discovery from library source code:
  1. Inspect what the user's project already imports
  2. Read the dependency source and docs in the project's installed packages
  3. Identify what functions, modifiers, hooks, and storage the dependency requires
  4. Apply those requirements to the user's contract
See Pattern Discovery and Integration below for the full step-by-step procedure.
主要工作流是从库源码中发现模式
  1. 检查用户项目已导入的内容
  2. 阅读项目已安装包中的依赖源码和文档
  3. 确定依赖所需的函数、修饰器、钩子函数和存储结构
  4. 将这些要求应用到用户的合约中
详情请见下文的【模式发现与集成】部分的完整分步流程。

MCP Generators as an Optional Shortcut

MCP生成器:可选快捷方式

If MCP generator tools are available at runtime, use them to accelerate pattern discovery: generate a baseline, generate with a feature enabled, compare the diff, and apply the changes to the user's code. This replaces the manual source-reading step but follows the same principle — discover patterns, then integrate them.
See MCP Generators (Optional) for details on checking availability and using the generate-compare-apply shortcut.
If no MCP tool exists for what's needed, use the generic pattern discovery methodology from Pattern Discovery and Integration. The absence of an MCP tool does not mean the library lacks support — it only means there is no generator.
如果运行时存在MCP生成器工具,可使用它们加速模式发现:生成基线版本、启用某特性生成版本、对比差异,然后将变更应用到用户代码中。这替代了手动阅读源码的步骤,但遵循相同的原则——发现模式,然后集成。
详情请见【MCP生成器(可选)】部分,了解如何检查工具可用性及使用“生成-对比-应用”快捷方式。
如果没有所需的MCP工具,使用【模式发现与集成】中的通用方法。缺少MCP工具并不意味着库不支持该特性——只是没有对应的生成器。

Pattern Discovery and Integration

模式发现与集成

Procedural guide for discovering and applying OpenZeppelin contract integration patterns by reading dependency source code. Works for any ecosystem and any library version.
Prerequisite: Always follow the library-first decision tree above (prefer library components over custom code, never copy/embed source).
这是通过阅读依赖源码来发现和应用OpenZeppelin合约集成模式的分步指南,适用于任何生态系统和任何库版本。
前提条件: 始终遵循上述的“以库为优先”决策树(优先使用库组件而非自定义代码,绝不复制/嵌入源码)。

Step 1: Identify Dependencies and Search the Library

步骤1:识别依赖并搜索库

  1. Search the project for contract files:
    Glob
    for
    **/*.sol
    ,
    **/*.cairo
    ,
    **/*.rs
    , or the relevant extension from the lookup table below.
  2. Read import/use statements in existing contracts to identify which OpenZeppelin components are already in use.
  3. Locate the installed dependency in the project's dependency tree:
    • Solidity:
      node_modules/@openzeppelin/contracts/
      (Hardhat/npm) or
      lib/openzeppelin-contracts/
      (Foundry/forge)
    • Cairo: resolve from
      Scarb.toml
      dependencies — source cached by Scarb
    • Stylus: resolve from
      Cargo.toml
      — source in
      target/
      or the cargo registry cache (
      ~/.cargo/registry/src/
      )
    • Stellar: resolve from
      Cargo.toml
      — same cargo cache locations as Stylus
  4. Browse the dependency's directory listing to discover available components. Use
    Glob
    patterns against the installed source (e.g.,
    node_modules/@openzeppelin/contracts/**/*.sol
    ). Do not assume knowledge of the library's contents — always verify by listing directories.
  5. If the dependency is not installed locally, clone or browse the canonical repository (see lookup table below).
  1. 搜索项目中的合约文件:使用
    Glob
    匹配
    **/*.sol
    **/*.cairo
    **/*.rs
    或下表中的相关扩展名。
  2. 阅读现有合约中的导入/使用语句,识别已在使用的OpenZeppelin组件。
  3. 在项目的依赖树中找到已安装的依赖:
    • Solidity:
      node_modules/@openzeppelin/contracts/
      (Hardhat/npm)或
      lib/openzeppelin-contracts/
      (Foundry/forge)
    • Cairo:从
      Scarb.toml
      依赖中解析——源码由Scarb缓存
    • Stylus:从
      Cargo.toml
      解析——源码位于
      target/
      或cargo注册表缓存(
      ~/.cargo/registry/src/
    • Stellar:从
      Cargo.toml
      解析——与Stylus的cargo缓存位置相同
  4. 浏览依赖的目录列表,发现可用组件。对已安装的源码使用
    Glob
    模式(如
    node_modules/@openzeppelin/contracts/**/*.sol
    )。不要假设了解库的内容——始终通过列目录来验证。
  5. 如果依赖未本地安装,克隆或浏览官方仓库(见下表)。

Step 2: Read the Dependency Source and Documentation

步骤2:阅读依赖源码和文档

  1. Read the source file of the component relevant to the user's request.
  2. Look for documentation within the source: NatSpec comments (
    ///
    ,
    /** */
    ) in Solidity, doc comments (
    ///
    ) in Rust and Cairo, and README files in the component's directory.
  3. Determine the integration strategy using the decision tree from the Critical Principle:
    • If the component satisfies the need directly → import and use as-is.
    • If customization is needed → identify extension points the library provides (virtual functions, hook functions, configurable constructor parameters). Import and extend.
    • Only if no component covers the need → write custom logic.
  4. Identify the public API: functions/methods exposed, events emitted, errors defined.
  5. Identify integration requirements — this is the critical step:
    • Functions the integrator MUST implement (abstract functions, trait methods, hooks)
    • Modifiers, decorators, or guards that must be applied to the integrator's functions
    • Constructor or initializer parameters that must be passed
    • Storage variables or state that must be declared
    • Inheritance or trait implementations required (always via import, never via copy)
  6. Search for example contracts or tests in the same repository that demonstrate correct usage. Look in
    test/
    ,
    tests/
    ,
    examples/
    , or
    mocks/
    directories.
  1. 阅读与用户需求相关的组件源码文件。
  2. 查找源码中的文档:Solidity中的NatSpec注释(
    ///
    /** */
    ),Rust和Cairo中的文档注释(
    ///
    ),以及组件目录中的README文件。
  3. 使用关键原则中的决策树确定集成策略:
    • 如果组件能直接满足需求 → 导入并按原样使用。
    • 如果需要自定义 → 识别库提供的扩展点(虚函数、钩子函数、可配置构造函数参数)。导入并扩展。
    • 仅当没有组件能满足需求时 → 编写自定义逻辑。
  4. 识别公共API:暴露的函数/方法、触发的事件、定义的错误。
  5. 识别集成要求——这是关键步骤:
    • 集成者必须实现的函数(抽象函数、trait方法、钩子函数)
    • 必须应用到集成者函数的修饰器、装饰器或守卫
    • 必须传递的构造函数或初始化器参数
    • 必须声明的存储变量或状态
    • 必须的继承或trait实现(始终通过导入,绝不复制)
  6. 在同一仓库中搜索演示正确用法的示例合约或测试。查找
    test/
    tests/
    examples/
    mocks/
    目录。

Step 3: Extract the Minimal Integration Pattern

步骤3:提取最小集成模式

From Step 2, construct the minimal set of changes needed:
  • Imports / use statements to add
  • Inheritance / trait implementations to add (always via import from the dependency)
  • Storage to declare
  • Constructor / initializer changes (new parameters, initialization calls)
  • New functions to add (required overrides, hooks, public API)
  • Existing functions to modify (add modifiers, call hooks, emit events)
If the contract is upgradeable, any of the above may affect storage compatibility. Consult the relevant upgrade skill before applying.
Do not include anything beyond what the dependency requires. This is the minimal diff between "contract without the feature" and "contract with the feature."
从步骤2中,构建所需的最小变更集:
  • 需要添加的导入/使用语句
  • 需要添加的继承/trait实现(始终通过依赖导入)
  • 需要声明的存储结构
  • 构造函数/初始化器变更(新参数、初始化调用)
  • 需要添加的新函数(必需的重写、钩子函数、公共API)
  • 需要修改的现有函数(添加修饰器、调用钩子函数、触发事件)
如果合约是可升级的,上述任何变更都可能影响存储兼容性。应用前请参考相关的升级技能文档。
不要包含依赖不需要的任何内容。这是“无特性合约”和“有特性合约”之间的最小差异。

Step 4: Apply Patterns to the User's Contract

步骤4:将模式应用到用户的合约

  1. Read the user's existing contract file.
  2. Apply the changes from Step 3 using the
    Edit
    tool. Do not replace the entire file — integrate into existing code.
  3. Check for conflicts: duplicate access control systems, conflicting function overrides, incompatible inheritance. Resolve before finishing.
  4. Do not ask the user to make changes themselves — apply directly.
  1. 阅读用户的现有合约文件。
  2. 使用
    Edit
    工具应用步骤3中的变更。不要替换整个文件——集成到现有代码中。
  3. 检查冲突:重复的访问控制系统、冲突的函数重写、不兼容的继承。完成前解决所有冲突。
  4. 不要让用户自行修改——直接应用变更。

Repository and Documentation Lookup Table

仓库与文档查找表

EcosystemRepositoryDocumentationFile ExtensionDependency Location
Solidityopenzeppelin-contractsdocs.openzeppelin.com/contracts
.sol
node_modules/@openzeppelin/contracts/
or
lib/openzeppelin-contracts/
Cairocairo-contractsdocs.openzeppelin.com/contracts-cairo
.cairo
Scarb cache (resolve from
Scarb.toml
)
Stylusrust-contracts-stylusdocs.openzeppelin.com/contracts-stylus
.rs
Cargo cache (
~/.cargo/registry/src/
)
Stellarstellar-contracts (Architecture)docs.openzeppelin.com/stellar-contracts
.rs
Cargo cache (
~/.cargo/registry/src/
)
生态系统仓库文档文件扩展名依赖位置
Solidityopenzeppelin-contractsdocs.openzeppelin.com/contracts
.sol
node_modules/@openzeppelin/contracts/
lib/openzeppelin-contracts/
Cairocairo-contractsdocs.openzeppelin.com/contracts-cairo
.cairo
Scarb缓存(从
Scarb.toml
解析)
Stylusrust-contracts-stylusdocs.openzeppelin.com/contracts-stylus
.rs
Cargo缓存 (
~/.cargo/registry/src/
)
Stellarstellar-contracts (Architecture)docs.openzeppelin.com/stellar-contracts
.rs
Cargo缓存 (
~/.cargo/registry/src/
)

Directory Structure Conventions

目录结构约定

Where to find components within each repository:
CategorySolidityCairoStylusStellar
Tokens
contracts/token/{ERC20,ERC721,ERC1155}/
packages/token/
contracts/src/token/
packages/tokens/
Access control
contracts/access/
packages/access/
contracts/src/access/
packages/access/
Governance
contracts/governance/
packages/governance/
packages/governance/
Proxies / Upgrades
contracts/proxy/
packages/upgrades/
contracts/src/proxy/
packages/contract-utils/
Utilities / Security
contracts/utils/
packages/utils/
,
packages/security/
contracts/src/utils/
packages/contract-utils/
Accounts
contracts/account/
packages/account/
packages/accounts/
Browse these paths first when searching for a component.
各仓库中组件的位置:
分类SolidityCairoStylusStellar
代币
contracts/token/{ERC20,ERC721,ERC1155}/
packages/token/
contracts/src/token/
packages/tokens/
访问控制
contracts/access/
packages/access/
contracts/src/access/
packages/access/
治理
contracts/governance/
packages/governance/
packages/governance/
代理/升级
contracts/proxy/
packages/upgrades/
contracts/src/proxy/
packages/contract-utils/
工具/安全
contracts/utils/
packages/utils/
,
packages/security/
contracts/src/utils/
packages/contract-utils/
账户
contracts/account/
packages/account/
packages/accounts/
搜索组件时优先浏览这些路径。

Known Version-Specific Considerations

已知版本特定注意事项

Do not assume override points from prior knowledge — always verify by reading the installed source. Functions that were
virtual
in an older version may no longer be in the current one, making them non-overridable. The source NatSpec will indicate the correct override point (e.g.,
NOTE: This function is not virtual, {X} should be overridden instead
).
A known example: the Solidity ERC-20 transfer hook changed between v4 and v5. Read the installed
ERC20.sol
to confirm which function is
virtual
before recommending an override.
不要假设重写点的先验知识——始终通过阅读已安装的源码来验证。旧版本中是
virtual
的函数在当前版本中可能不再是,因此无法重写。源码中的NatSpec会指明正确的重写点(例如:
注意:此函数不是virtual,应重写{X}代替
)。
一个已知示例:Solidity ERC-20的转账钩子在v4和v5之间发生了变化。建议重写前先阅读已安装的
ERC20.sol
以确认哪个函数是
virtual

MCP Generators (Optional)

MCP生成器(可选)

MCP generators are template/scaffolding tools that produce OpenZeppelin contract boilerplate. They are not required — they accelerate pattern discovery when available.
MCP生成器是生成OpenZeppelin合约模板的脚手架工具。它们不是必需的——仅在可用时加速模式发现。

Checking Availability

检查可用性

Discover MCP tools dynamically at runtime. Look for tools with names matching patterns like
solidity-erc20
,
cairo-erc721
,
stellar-fungible
, etc. Server names follow patterns like
OpenZeppelinSolidityContracts
,
OpenZeppelinCairoContracts
, or
OpenZeppelinContracts
.
MCP tool schemas are self-describing. To learn what a generator supports, inspect its parameter list — each boolean parameter (e.g.,
pausable
,
mintable
,
upgradeable
) corresponds to a feature toggle. Do not rely on prior knowledge of what parameters exist; read the schema each time, since tools are updated independently of this skill.
在运行时动态发现MCP工具。查找名称匹配
solidity-erc20
cairo-erc721
stellar-fungible
等模式的工具。服务名称遵循
OpenZeppelinSolidityContracts
OpenZeppelinCairoContracts
OpenZeppelinContracts
等模式。
MCP工具的 schema 是自描述的。要了解生成器支持的功能,检查其参数列表——每个布尔参数(如
pausable
mintable
upgradeable
)对应一个特性开关。不要依赖先验知识来了解参数;每次都要读取schema,因为工具的更新独立于此技能。

Generate-Compare-Apply Shortcut

生成-对比-应用快捷方式

When an MCP generator exists for the contract type:
  1. Generate baseline — call with only required parameters, all features disabled
  2. Generate with feature — call again with one feature enabled
  3. Compare — diff baseline vs. variant to identify exactly what changed (imports, inheritance, state, constructor, functions, modifiers)
  4. Apply — edit the user's existing contract to add the discovered changes
For interacting features (e.g., access control + upgradeability), generate a combined variant as well.
当存在对应合约类型的MCP生成器时:
  1. 生成基线版本——仅传递必需参数,禁用所有特性
  2. 生成带特性的版本——再次调用,启用一个特性
  3. 对比——对比基线版本和变体版本,确定具体变更(导入、继承、状态、构造函数、函数、修饰器)
  4. 应用——编辑用户的现有合约,添加发现的变更
对于交互特性(如访问控制+可升级性),还需生成组合变体版本。

When No MCP Tool Exists or a Feature Is Not Covered

当没有MCP工具或特性未被覆盖时

The absence of an MCP tool does NOT mean the library lacks support. It only means there is no generator for that contract type. Always fall back to the generic pattern discovery methodology in Pattern Discovery and Integration.
Similarly, when an MCP tool exists but does not expose a parameter for a specific feature, do not stop there. Fall back to pattern discovery for that feature: read the installed library source to find the relevant component, extract the integration requirements, and apply them to the user's contract.
缺少MCP工具不意味着库不支持该特性。仅意味着没有对应合约类型的生成器。始终回到【模式发现与集成】中的通用模式发现方法。
同样,当MCP工具存在但未暴露特定特性的参数时,不要就此停止。回到该特性的模式发现:阅读已安装的库源码找到相关组件,提取集成要求,并应用到用户的合约中。