本文介绍了如何创建、部署和测试符合EIP-2535标准的Diamond智能合约,使用了diamond-1-hardhat库和louper.dev工具,详细说明了所需的项目设置和代码编写过程,适合对智能合约开发有一定了解的读者。
本指南包含对 Goerli 测试网的引用,该测试网已不再积极维护。虽然与此链相关的特定步骤可能不适用,但整体流程可能适用于其他链。我们建议探索你实现的当前替代方案。如果你希望查看本指南的更新版本,请 告诉我们!
在 钻石标准系列的第一部分 中,我们学习了钻石合约(即多面代理)及其工作原理。在本指南中,你将学习如何创建、部署并测试一个符合 EIP-2535 的钻石智能合约,使用 diamond-1-hardhat 仓库和 louper.dev,这是一个用于检查 EVM 基链上钻石智能合约的工具。
配置和检查 diamond-1-hardhat 仓库
创建并部署一组钻石智能合约
测试钻石智能合约功能
在本教程中,我们将使用 diamond-1-hardhat 仓库(由 [Nick Mudge](https://github.com/quiknode-labs/guides/blob/71cfd6a37d599d9bedd17216158713c593e8ddef/sc9743)创建,EIP-2535 的作者)创建一个钻石智能合约。如果你想查看其他实现,请查看这些其他 资源。请注意,还有 diamond-2-hardhat 和 diamond-3-hardhat 仓库,展示了实现和部署钻石智能合约的替代方法(例如,Gas优化和实现 loupe 函数)。
现在,让我们开始设置项目。打开终端窗口并运行以下命令以克隆该仓库并导航至其中:
git clone https://github.com/mudgen/diamond-1-hardhat && cd diamond-1-hardhat
然后,安装所需的依赖项:
npm install && npm install --save-dev @nomiclabs/hardhat-etherscan
在你选择的代码编辑器中打开项目,并打开 hardhat.config.js 文件。在文件顶部添加以下导入:
require("@nomiclabs/hardhat-etherscan");
需要插件以使用 Etherscan 自动验证智能合约
然后,将以下代码添加到 hardhat.config.js 文件中 module.exports 对象的末尾:
networks: {
goerli: {
url: "YOUR_QUICKNODE_HTTP_ENDPOINT",
accounts: ["YOUR_PRIVATE_KEY"]
}
},
etherscan: {
// 你的 Etherscan API 密钥
// 可以在 https://etherscan.io/ 获取
apiKey: "YOUR_ETHERSCAN_API_KEY"
}
现在我们将花一点时间以确保我们在测试网 Goerli 上有足够的 ETH,并用我们实际的 RPC URL、私钥和 Etherscan API 密钥填充上述占位符值。
首先,检索你的私钥并将其替换为上面的 YOUR_PRIVATE_KEY(如果你使用的是 MetaMask,请在 此处 查找说明)。接下来,导航至 QuickNode Multi-Chain Faucet 在 Ethereum Goerli 测试网上获取一些 ETH。你可以连接钱包或粘贴你的公钥地址;但是请注意,你需要至少 0.001 ETH 在以太坊主网才能使用测试网水龙头。
你还需要输入来自 Etherscan.io 的 API 密钥(我们稍后将在验证我们的 Facet 时使用 louper.dev)。最后,我们需要访问以太坊 Goerli 测试网区块链。你可以使用公共节点或部署和管理你自己的基础设施;但是,如果你想要最高提升 8 倍的响应时间,可以把繁重的工作留给我们。点击 这里 注册一个免费的账户。获取 RPC URL 后,使用 hardhat.config.js 中的 YOUR_QUICKNODE_HTTP_ENDPOINT 替换它,并保存该文件。
完成所有这些任务后,你现在已经配置好我们的 Hardhat 项目以在以太坊 Goerli 测试网上部署和验证源代码,使用 Hardhat-etherscan 插件。接下来,我们将创建并部署一组钻石智能合约。
diamond-1-hardhat 仓库是 EIP-2535 Diamonds 的参考实现。它包含创建一个钻石合约所需的样板代码。它甚至有测试 Facets 你可以使用;然而,为了学习的目的,我们将创建自己的 Facet。
在你的 contracts/facets 文件夹中,创建一个 FacetA.sol 文件,并添加以下代码:
pragma solidity ^0.8.0;
library LibA {
struct DiamondStorage {
address owner;
bytes32 dataA;
}
function diamondStorage() internal pure returns(DiamondStorage storage ds) {
bytes32 storagePosition = keccak256("diamond.storage.LibA");
assembly {
ds.slot := storagePosition
}
}
}
contract FacetA {
function setDataA(bytes32 _dataA) external {
LibA.DiamondStorage storage ds = LibA.diamondStorage();
ds.dataA = _dataA;
}
function getDataA() external view returns (bytes32) {
return LibA.diamondStorage().dataA;
}
}
让我们回顾一下代码!
行 1:设置我们的版本声明。请注意,设置版本指示编译器检查版本是否与声明所要求的版本匹配。如果不匹配,编译器将发出错误。
行 3-17:创建一个实现 DiamondStorage 的库。该库包含一个结构体来存储 Facets 变量,并且有一个 diamondStorage 函数指定一个从字符串哈希中生成的随机位置,并将该结构体在合约存储中的位置进行设置。
行 19-28:创建一个合约,实现两个函数,setDataA,该函数接受一个 bytes32 值并设置上面定义的钻石存储,以及 getDataA,该函数从设置的存储中读取。
现在我们已经创建了我们自己的 Facet,我们可以开始编译和部署智能合约。部署钻石智能合约并添加我们的 Facet 的过程将如下所示:
部署 DiamondInit.sol
部署 DiamondCutFacet.sol
部署 DiamondLoupeFacet.sol
部署 OwnershipFacet.sol
部署 Diamond.sol
部署 FacetA.sol
调用 diamondCut 函数以添加 FacetA
在你的终端中,导航到根项目目录,并运行命令 npx hardhat compile,你应该会得到类似的响应:
然后要部署合约,运行命令:npx hardhat run scripts/deploy.js --network goerli。可能需要几分钟才能部署,但完成后,你将在终端中看到每个部署合约地址的输出。
接下来,我们将部署 Facet 合约。在你的 scripts 文件夹中,创建一个名为 deployFacet.js 的文件,并输入以下代码:
/* global ethers */
/* eslint prefer-const: "off" */
async function deployFacet() {
const FacetA = await ethers.getContractFactory('FacetA');
const facetA = await FacetA.deploy();
await facetA.deployed();
console.log('facetA 部署成功:', facetA.address);
return facetA.address;
}
// 我们建议使用此模式,以便可以在各处使用 async/await
// 并正确处理错误。
deployFacet().catch((error) => {
console.error(error);
process.exitCode = 1;
});
保存该文件,并运行命令 npx hardhat run scripts/deployFacet.js --network goerli 进行部署。你的输出应包含已部署的 FacetA 合约的地址:
在将我们的 Facet 添加到钻石之前,让我们在 Etherscan 上验证源代码。运行以下命令,并用你的 FacetA 智能合约地址替换:
npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS
你会收到一个链接,指向一个 Etherscan 页面,你可以在其中查看已验证的源代码。
现在,所有必要的合约都已部署,你需要使用 diamondCut 函数将你的 Facet 合约(FacetA.sol)添加到你的钻石中。我们将使用 louper.dev(用于检查和交互钻石智能合约的工具)来做到这一点。导航至 louper.dev 并输入你的钻石合约地址和你部署的网络(例如,goerli)。
然后,点击 编辑 选项卡并单击 连接钱包 按钮。连接钱包后,单击 添加 Facet 按钮,输入你之前部署的 FacetA 地址。然后,点击 获取 Facet ABI 按钮。
单击 获取 Facet 信息 后,选择这两个功能并点击 升级钻石(你需要在你的钱包中批准该交易)。
此交易调用钻石合约上的 diamondCut
函数。
一旦你的交易被确认,你可以继续下一部分,在那里你将测试新添加的 Facet 在你的钻石合约上。
现在我们的钻石合约了解了我们的 FacetA 合约,我们可以测试 setDataA 和 getDataA 函数。导航至你在 louper.dev 上的钻石合约的 写入 选项卡,并连接你的钱包。在下拉菜单中选择 FacetA,然后点击展开部分并输入值 0x68656c6c6f206469616d6f6e6473000000000000000000000000000000000000(这在解码字符串格式中转换为“hello diamonds”)。最后,在你的 MetaMask 钱包中签署该交易。
一旦你的交易被确认,前往你在 louper.dev 上的钻石合约的 读取 选项卡。接下来,从下拉列表中选择 FacetA,并点击 查询 按钮,获取 getDataA facet。你应该能看到你之前设置的字符串!
恭喜!你现在对钻石标准及如何实现它有了更多了解。有什么想法,问题,或者想展示你的学习成果?请在 Discord 上告诉我们,或通过 Twitter 与我们联系。
如果你对本指南有任何反馈或问题,请 告诉我们。我们很乐意听取你的意见!
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!