这篇文章详细介绍了以太坊的钻石标准(EIP-2535),包括其基本概念、核心组件及其优势。文章结构清晰,包含了对钻石合约及其各个组成部分的深入分析,以帮助读者理解如何模块化和升级智能合约。
智能合约开发如果需要维护大量合约逻辑,可能会变得相当复杂。Diamond 标准 ( EIP-2535)使得模块化以及高效升级和管理智能合约变得更加简单。在这个两部分系列的第一部分中,你将了解什么是 Diamond 标准以及它是如何工作的。然后在 第二部分 中,你将学习如何使用 Hardhat 创建和部署符合 EIP-2535 的 Diamond 智能合约。
了解什么是 Diamond 标准
了解 Diamond 标准中的每个组件
探讨 Diamond 标准的好处
Diamond 标准是一个终版的以太坊改进提案 ( EIP-2535),旨在使开发者更容易模块化和升级他们的智能合约。Diamond 标准的核心思想类似于可升级智能合约,例如代理模式,但其优势在于你可以从单个 Diamond 合约 (即代理合约) 控制多个实现合约 (即逻辑合约)。Diamond 标准的一些关键特性包括:
一个单一的网关可以对 n 个实现合约进行代理调用
可原子升级单个或多个智能合约
对于你可以添加到 Diamond 的实现合约数量没有存储限制
所有对 Diamond 进行的升级都有日志历史
可以减少Gas成本 (即,通过减少外部函数调用的数量)
Diamond 合约的三个核心组件包括 DiamondCut、DiamondStorage 和标准函数及事件,它们允许你查看 Diamond 中的内容以及何时进行了升级。一些已实现 Diamond 标准的协议包括 Aavegotchi、BarnBridge、DerivaDEX 和 Oncyber。
此外,还有不同类型的 Diamonds,例如:
可升级 Diamond:一个可变合约,可以进行升级
完成 Diamond:由于去除了可升级性特性而成为不可变合约
单切割 Diamond:一个不可变合约,无法再进行升级
现在你对该标准有了一个高层次的理解,让我们深入一些构成该标准的组件。
Diamond 是一个智能合约,使用其他智能合约(即 Facets)进行 delegatecall 调用。什么是 delegate call 和 Facet?简而言之,delegate call 是一个特殊的外部函数调用,其中代理合约从另一个智能合约借用代码,但在其自身的上下文中 (即,它自己的状态变量)。Diamond 合约的另一个核心组件是 Facets,它允许你将实现逻辑模块化成多个独立的 Solidity 文件,并根据需要进行升级。接下来,我们将详细了解 Facets。
Facet 可以与实现合约或库进行比较,因为它包含代理(例如 Diamond)合约将进行调用的外部函数逻辑。添加到 Diamond 的 Facet 数量没有限制,这在标准代理模式中是不可行的。Diamond 合约中 Facets 的好处是它允许你模块化代码,并仅更新所需的部分。与常见的代理模式不同,在该模式下你需要重新部署一个新的实现合约,使用 Facets 时,由于你可以拥有多个实现合约,你可以更新多个 Facets(例如,多个实现合约)或单个 Facet。
你可能会想知道 Facets 是如何存储在 Diamond 中的。好问题。Facets 是与你的 Diamond 合约分开部署的,可以通过 DiamondCut (我们稍后会讲到) 将其添加到 Diamond。这种方式可以高效组织代码,并仅通过指向新的 Facet 地址更新所需的 Facet。这些地址存储在一个映射中(通常称为 selectorToFacet),它持有 (bytes4 => address) 的映射。这个 bytes4 值表示你希望在它所指向的 Facet 地址调用的函数签名。
现在让我们谈谈内部机制。当在 Diamond 上调用外部函数时,Diamond 合约的回退函数会查看使用 selectorToFacet 映射调用的是什么函数。然后,它会进行 delegatecall 以在相应的 Facet 地址上执行该函数。如果外部函数调用有返回值,Diamond 合约将把这些返回给你。
一个典型的 Facet 合约可能看起来像这样:
contract FacetA {
function setDataA(bytes32 _dataA) external {
LibA.DiamondStorage storage ds = LibA.diamondStorage();
require(ds.owner == msg.sender, "必须是所有者。");
ds.dataA = _dataA;
}
function getDataA() external view returns (bytes32) {
return LibA.diamondStorage().dataA;
}
}
DiamondCut 是必须在 Diamond 合约中实现的标准函数。它允许你从 Diamond 合约中添加、替换或移动函数。DiamondCut 可以视为“升级”功能,你可以更新存储中 Diamond 合约的映射(例如 selectorToFacet),以指向新的函数签名和/或地址。每当你调用 DiamondCut 函数时,将触发一个事件,可用于跟踪你的升级历史。
以下是 EIP-2535 网页中的一个示例,展示了 DiamondCut 接口的外观:
interface IDiamondCut {
enum FacetCutAction {Add, Replace, Remove}
struct FacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
function diamondCut(
FacetCut[] calldata _diamondCut,
address _init,
bytes calldata _calldata
) external;
event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}
接下来,你将了解 Diamond 合约中状态变量的存储选项。
AppStorage 和 DiamondStorage
在你的 Diamond 中整合存储有多种方法。最常见的两种是使用 AppStorage 和 DiamondStorage。由于本指南的第二部分将实现 AppStorage,因此我们将很简单地介绍 DiamondStorage。DiamondStorage 技术包括在结构体中声明你的状态变量。那些结构体在合约存储中具有特定位置。
AppStorage 是管理状态变量和存储布局的另一种方法。这种方法更高效,因为它帮助你在 Facets 之间共享状态变量。使用 AppStorage,你创建一个包含所有状态变量结构的智能合约。一旦部署,你可以将该 AppStorage 导入多个 Facets。
一个实现 AppStorage 的 Facet 的示例可能如下所示:
import "./AppStorage.sol"
contract StakingFacet {
AppStorage internal s;
function myFacetFunction(uint256 _nextVar) external {
s.total = s.firstVar + _nextVar;
}
注意:AppStorage 通常定义为 s
Diamond 合约实现了 DiamondLoupe 接口。DiamondLoupe 帮助我们查看 Diamond 合约中 Facets、函数选择器和 Facet 地址的指向情况。以下是 DiamondLoupe 接口的一个示例:
interface IDiamondLoupe {
struct Facet {
address facetAddress;
bytes4[] functionSelectors;
}
function facets() external view returns (Facet[] memory facets_);
function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_);
function facetAddresses() external view returns (address[] memory facetAddresses_);
function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_);
}
这些函数旨在离线使用,不需要Gas费。
恭喜!你现在对 Diamond 标准有了更好的理解。随着合约系统需要更复杂化,以及支持该标准的工具的出现,Diamonds 在未来应会得到更多应用。在 第二部分 中,你将学习如何部署一个符合 Diamond 标准的智能合约并与之交互。如果你还想了解更多关于 Diamond 标准的信息,请查看 EIP2535 仓库的 学习与资源 部分,该仓库由 Nick Mudge(Diamond 标准的创建者)创建!
如果你对本指南有任何反馈或问题, 请告诉我们。我们很乐意听取你的意见!
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!