本指南深入探讨了Ethereum的CREATE2功能,它使开发者能够在部署智能合约之前预测其地址,并展示了CREATE2的多种实用案例。此外,通过设置开发环境并提供详细的代码示例,读者能够掌握如何实现这一主要功能。
区块链技术持续发展,为开发者提供新的工具和技术。在这种增长中,以太坊引入了 CREATE2,这一特性可以增强智能合约的使用方式。本指南深入探讨了CREATE2的复杂性,解释并展示了其实际应用案例。
通过本指南的学习,你将深入理解如何利用这一特性,并将其纳入你的以太坊开发者知识库。
依赖 | 版本 |
---|---|
Node.js | 18.18.0 |
npm | 7≥ |
@nomicfoundation/hardhat-toolbox | ^3.0.0 |
@openzeppelin/contracts | ^4.9.3 |
dotenv | ^16.3.1 |
目前,每当部署一个智能合约时,它会收到一个无法在部署前预先确定的随机地址。CREATE2,更中的技术名称是 Skinny CREATE2,是一种操作码,允许开发者在部署之前预先确定智能合约的地址。它是以太坊的 Constantinople升级 的一部分引入的。
值得注意的是,技术上可以通过 nonce 方法来预先确定一个地址。这有一些缺点,例如跟踪nonce(以确保你传入一个有效的值)和必须生成 N
个私钥,以及资助一个不打算部署的地址的脆弱性,但我们将在本指南中不讨论这种方法。
CREATE2的核心组件由一个 常量 值、部署者地址、salt(任意值)和 字节码(编译的solidity代码)组成。其公式如下:
address = hash(0xFF, sender, salt, bytecode)
注意
请注意,CREATE2仍然容易受到前置运行攻击。为了应对这一点,开发者可以使用他们的钱包地址(即msg.sender)作为 salt 的 N 部分,以确保它是唯一的且无法伪造。
让我们继续深入研究。
此外,CREATE2启用的一些用例包括:
现在我们对CREATE2及其用例有了基本理解。接下来,让我们进行开发环境的设置,然后进行编码!
为了与以太坊网络进行通信,我们需要访问一个RPC端点。我们不打算自己运行节点并管理其基础设施,而是将繁重的工作交给QuickNode。QuickNode提供了高达8x的响应速度。你可以在 这里 创建一个免费账户。
登录后,选择 创建一个端点 并选择 Ethereum Sepolia Testnet 区块链。创建后,复制 HTTP Provider URL 并保留以备后用,因为你将在后续的部分中需要它。
打开终端窗口,运行以下命令以创建项目文件夹、初始化Hardhat,并创建必要的文件/文件夹。
mkdir create2-hardhat
cd create2-hardhat
npx hardhat
选择 Empty hardhat.config.js 选项,然后继续运行以下命令以创建所需的文件:
mkdir contracts
mkdir scripts
echo > contracts/ArtFactory.sol && echo > contracts/ArtworkToken.sol
echo > scripts/deployFactory.js && echo > scripts/deployArtToken.js
echo > .env
然后,让我们安装依赖:
npm install @openzeppelin/contracts @nomicfoundation/hardhat-toolbox dotenv
如果你想以后将项目推送到Github,我们将使用 dotenv 库。请记住,如果你这样做,还需要将其包含在你的 .gitignore 中。
打开 hardhat.config.js
文件并输入以下配置:
/** @type import('hardhat/config').HardhatUserConfig */
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config()
module.exports = {
defaultNetwork: "sepolia",
solidity: "0.8.19",
networks: {
hardhat: {
},
sepolia: {
url: process.env.RPC_URL,
accounts: [process.env.PRIVATE_KEY]
}
}
};
最后,打开 .env 文件,输入以下占位符,并使用你的私密凭证填充它。
RPC_URL=YOUR_QUICKNODE_ENDPOINT
PRIVATE_KEY=YOUR_PRIVATE_KEY
记住保存文件,然后继续到下一部分!
现在,我们的开发环境已经设置好,让我们在 contracts
和 scripts
文件夹中设置逻辑。请注意,我们不会向你展示如何部署一个智能合约,但你可以在 这里 查看快速示例。
让我们打开 ArtFactory.sol
文件并输入以下代码:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "@openzeppelin/contracts/utils/Create2.sol";
import "./ArtworkToken.sol";
contract ArtworkTokenFactory {
address public latestTokenAddress;
mapping(bytes32 => address) public deployedArtworks;
modifier isArtworkNotDeployed(bytes32 _salt) {
require(deployedArtworks[_salt] == address(0), "该salt已经部署了艺术作品代币");
_;
}
function deployToken(bytes32 _salt, uint8 _artworkID)
external
isArtworkNotDeployed(_salt)
returns (address)
{
latestTokenAddress = Create2.deploy(
0,
_salt,
abi.encodePacked(type(ArtworkToken).creationCode, abi.encode(_artworkID))
);
deployedArtworks[_salt] = latestTokenAddress;
return latestTokenAddress;
}
function computeTokenAddress(bytes32 _salt, uint8 _artworkID)
public
view
returns (address)
{
return Create2.computeAddress(
_salt,
keccak256(abi.encodePacked(type(ArtworkToken).creationCode, abi.encode(_artworkID)))
);
}
}
让我们回顾一下代码。
利用 OpenZeppelin的 Create2 工具,以上代码允许预测代币地址,甚至在它们被部署之前,确保每个艺术作品代币都有一个独特的地址。用户可以通过唯一标识符和特定salt来部署新的艺术作品代币。智能合约还维护着所有已部署艺术品的记录,确保不会使用相同的salt创建重复的代币。
然后,打开 ArtworkToken.sol
并输入以下代码:
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
contract ArtworkToken {
uint8 public immutable artworkID;
constructor(uint8 _artworkID) {
artworkID = _artworkID;
}
}
让我们回顾一下代码。
在上述合约中,每个代币由一个不可变的 artworkID 特征,其在代币创建时被赋值。该ID作为每件数字艺术品在代币中表示的独特标识符。
现在,我们将为每个脚本填写逻辑。打开 deployFactory.js
文件,输入以下代码:
const hre = require('hardhat');
const { ethers } = require('ethers');
require('dotenv').config();
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
async function deployArtworkTokenFactory() {
try {
const ArtworkTokenFactory = await hre.ethers.getContractFactory('ArtworkTokenFactory', signer);
console.log('正在部署 ArtworkTokenFactory...');
const factory = await ArtworkTokenFactory.deploy();
await factory.waitForDeployment();
console.log('ArtworkTokenFactory 部署到:', factory.target);
return factory.target;
} catch (err) {
console.error('在 deployArtworkTokenFactory 中出错:', err);
}
}
async function main() {
await deployArtworkTokenFactory();
}
main().catch(err => console.error('主函数中出错:', err));
接下来,我们将为 deployArtToken.js
脚本创建逻辑。打开文件并输入以下代码:
const hre = require('hardhat');
const { ethers } = require('ethers');
require('dotenv').config();
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL)
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider)
const artFactoryContractAddress = 'YOUR_ART_FACTORY_ADDRESS';
const deployerAddress = signer.address;
const deployerBytes = ethers.getBytes(deployerAddress).slice(0, 20);
const randomString = "wen"; // 这个值必须在每次ArtworkToken部署时更改
const randomBytes = ethers.toUtf8Bytes(randomString);
const concatenatedBytes = ethers.concat([deployerBytes, randomBytes]);
// 通过哈希连接字节生成salt
const salt = ethers.keccak256(concatenatedBytes);
const artworkID = 42; // 示例ID
async function deployArtworkToken() {
try {
const ArtworkTokenFactory = await hre.ethers.getContractAt('ArtworkTokenFactory', artFactoryContractAddress, signer);
// 在部署之前计算预期地址
const expectedAddress = await ArtworkTokenFactory.computeTokenAddress(salt, artworkID);
console.log('预期 ArtworkToken 地址:', expectedAddress);
// 使用 ArtworkTokenFactory 部署 ArtworkToken
const txn = await ArtworkTokenFactory.deployToken(salt, artworkID);
await txn.wait()
const tokenAddress = await ArtworkTokenFactory.latestTokenAddress()
console.log('已部署 ArtworkToken 地址:', tokenAddress);
if (expectedAddress == tokenAddress) {
console.log("预期和已部署地址匹配,CREATE2功能已验证!");
} else {
console.error("预期和已部署地址不匹配!");
}
} catch (err) {
console.error('在 deployArtworkToken 中出错:', err);
}
}
async function main() {
await deployArtworkToken();
}
main().catch(err => console.error('主函数中出错:', err));
记住保存这两个文件!在下一部分,我们将在Sepolia测试网上部署并测试智能合约。
现在是看到CREATE2实际运行的时候了。在你的项目的主目录(例如create2-hardhat)中,运行以下命令首先部署工厂合约。
npx hardhat run --network sepolia scripts/deployFactory.js
你将看到类似以下的输出:
正在部署 ArtworkTokenFactory...
ArtworkTokenFactory 部署到: 0x0bD0eD146A59e32B8840D264E61DD7df0a3E6e84
然后,复制在终端上输出的 ArtworkTokenFactory 地址,并将其分配给其他脚本中的 artFactoryContractAddress 变量(例如,deployArtToken.js)。
执行下面的脚本以在部署艺术代币的同时预测它的合约地址,并在其被部署后验证它。
npx hardhat run --network sepolia scripts/deployArtToken.js
你将看到类似以下的输出:
预期 ArtworkToken 地址: 0xa212ae6E8b57CA67a4b55686E208b9aDC5F3023C
已部署 ArtworkToken 地址: 0xa212ae6E8b57CA67a4b55686E208b9aDC5F3023C
预期和已部署地址匹配,CREATE2功能已验证!
注意:如果你尝试在不更改
randomString
值的情况下重新运行上面的脚本,交易将会回滚(假设这个salt已经用来创建合约)。你可以测试以观察其实际效果。
我们可以看到,在部署之前,预测的地址与部署到区块链上的地址一致。做得好!在下一部分,我们将通过给你一个简短的测验来测试你的知识!祝你好运!
请尝试下面的短测验!
🧠知识检查
"Skinny CREATE2" 引入了哪个操作码?
0xf4 0xf0 0xf5 0xf6
你现在已经对CREATE2有了深入了解!利用这个操作码为智能合约开发增加了一层新的可预测性。继续探索并将你新获得的知识应用于实际项目中。
如果你有任何问题,请随时通过 Discord 使用我们的专用频道或使用下面的表单提供反馈。通过关注我们的 Twitter 和我们的 Telegram公告频道 来保持最新状态。
告诉我们 如果你有任何反馈或对新主题的请求。我们很想听到你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!