本文档介绍了如何在Solidity中使用OpenZeppelin Contracts进行合约扩展。主要讨论了通过继承进行功能扩展和通过重写(override)改变父合约行为的方法,包括如何调用super
来扩展父合约的行为。同时强调了自定义重写可能带来的安全风险,并建议开发者在更新OpenZeppelin Contracts版本时重新验证其假设。
大多数 OpenZeppelin 合约预期通过 继承 来使用:在编写你自己的合约时,你将从它们那里继承。
这是常见的 is
语法,比如 contract MyToken is ERC20
。
与 contract 不同,Solidity library 不会被继承,而是依赖于 using for 语法。<br>OpenZeppelin 合约有一些 library :大多数在 Utils 目录中。 |
继承通常用于将父合约的功能添加到你自己的合约中,但这并不是它唯一能做的。你也可以使用重写来改变父合约某些部分的表现。
例如,假设你想改变 AccessControl
,使得 revokeRole
不能再被调用。这可以通过重写来实现:
// contracts/AccessControlModified.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
contract AccessControlModified is AccessControl {
error AccessControlNonRevocable();
// Override the revokeRole function
function revokeRole(bytes32, address) public pure override {
revert AccessControlNonRevocable();
}
}
旧的 revokeRole
随后被我们的重写所取代,任何对它的调用都会立即 revert。我们不能从合约中移除这个函数,但是在所有调用时都 revert 已经足够了。
super
有时你想扩展父合约的行为,而不是彻底地将其改变为其他的东西。这就是 super
的用武之地。
即使一个函数被重写了,super
关键字也能让你调用定义在父合约中的函数。这种机制可以用于向函数添加额外的检查、发出事件,或者以你认为合适的方式添加功能。
想要了解更多关于重写是如何工作的,请查阅 官方 Solidity 文档。 |
这里是一个修改后的 AccessControl
版本,其中 revokeRole
不能用于撤销 DEFAULT_ADMIN_ROLE
:
// contracts/AccessControlNonRevokableAdmin.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
contract AccessControlNonRevokableAdmin is AccessControl {
error AccessControlNonRevokable();
function revokeRole(bytes32 role, address account) public override {
if (role == DEFAULT_ADMIN_ROLE) {
revert AccessControlNonRevocable();
}
super.revokeRole(role, account);
}
}
结尾的 super.revokeRole
语句将会调用 AccessControl
原始版本的 revokeRole
,也就是在没有重写的情况下会运行的相同代码。
相同的规则在 AccessControlDefaultAdminRules 中被实现和扩展,这是一个也为 DEFAULT_ADMIN_ROLE 增加了强制安全措施的扩展。 |
OpenZeppelin 合约的维护者主要关注库中发布的代码的正确性和安全性,以及基础合约与库中官方扩展的组合。
自定义重写,特别是对 hooks 的重写,可能会扰乱重要的假设,并可能在以前安全的代码中引入安全风险。虽然我们尽力确保合约在面对各种潜在的自定义时保持安全,但这是尽最大努力完成的。虽然我们试图记录所有重要的假设,但你不应该依赖这些假设。自定义重写应该仔细审查,并对照它们正在自定义的合约的源代码进行检查,以充分理解它们的影响并保证它们的安全性。
函数在内部交互的方式不应被假定为在库的各个版本中保持稳定。例如,在一个特定版本中的某个上下文中使用的函数可能不会在下一个版本中在相同的上下文中使用。重写函数的合约应该在更新它们所构建的 OpenZeppelin 合约的版本时重新验证它们的假设。
- 原文链接: docs.openzeppelin.com/co...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!