扩展合约

大多数 OpenZeppelin 合约期望通过 继承 来使用:在编写自己的合约时,你将 继承 它们。

这是常见的 is 语法,例如 contract MyToken is ERC20

contract 不同,Solidity library 不会被继承,而是依赖于 using for 语法。

OpenZeppelin 合约有一些 library:大多数在 Utils 目录中。

覆盖 (Overriding)

继承通常用于将父合约的功能添加到你自己的合约中,但这并不是它的全部功能。你还可以使用 覆盖(overrides)更改 父合约某些部分的运行方式。

例如,假设你想要更改 AccessControl,以使 revokeRole 不能再被调用。这可以通过覆盖来实现:

Unresolved include directive in modules/ROOT/pages/extending-contracts.adoc - include::api:example$access-control/AccessControlModified.sol[]

旧的 revokeRole 随后被我们的覆盖所替换,并且任何对其的调用都将立即还原(revert)。 我们无法从合约中 删除 该函数,但是在所有调用上都还原(revert)就足够了。

调用 super

有时你想要 扩展 父级的行为,而不是完全将其更改为其他内容。 这就是 super 的用武之地。

super 关键字将允许你调用在父合约中定义的函数,即使它们被覆盖也是如此。 此机制可用于向函数添加其他检查、发出事件或根据需要添加功能。

有关覆盖如何工作的更多信息,请访问 官方 Solidity 文档

这是一个 AccessControl 的修改版本,其中 revokeRole 不能用于撤销(revoke) DEFAULT_ADMIN_ROLE

Unresolved include directive in modules/ROOT/pages/extending-contracts.adoc - include::api:example$access-control/AccessControlNonRevokableAdmin.sol[]

末尾的 super.revokeRole 语句将调用 AccessControl 的原始版本 revokeRole,即如果不存在覆盖,则会运行的相同代码。

相同的规则在 AccessControlDefaultAdminRules 中实现和扩展,该扩展还为 DEFAULT_ADMIN_ROLE 添加了强制安全措施。

安全

OpenZeppelin 合约的维护者主要关注库中发布的代码以及基本合约与库中的官方扩展的组合的正确性和安全性。

自定义覆盖,尤其是对钩子的覆盖,可能会破坏重要的假设,并可能在以前安全的代码中引入安全风险。 尽管我们尽力确保合约在面对各种潜在的自定义设置时保持安全,但这是尽最大努力完成的。 尽管我们尝试记录所有重要的假设,但不能依赖于此。 应对自定义覆盖进行仔细审查,并对照其自定义的合约的源代码进行检查,以充分了解其影响并保证其安全性。

函数在内部交互的方式不应被假定为在库的各个版本中保持稳定。 例如,在特定版本中的一个上下文中使用的函数可能不会在下一个版本中的相同上下文中使用。 覆盖函数的合约在更新其构建的 OpenZeppelin 合约的版本时,应重新验证其假设。