以太坊 - 使用Solidity的CREATE2在多个网络上预测合约地址 - Quicknode

  • QuickNode
  • 发布于 2024-04-11 17:24
  • 阅读 13

本指南深入探讨了Ethereum的CREATE2功能,它使开发者能够在部署智能合约之前预测其地址,并展示了CREATE2的多种实用案例。此外,通过设置开发环境并提供详细的代码示例,读者能够掌握如何实现这一主要功能。

概述

区块链技术持续发展,为开发者提供新的工具和技术。在这种增长中,以太坊引入了 CREATE2,这一特性可以增强智能合约的使用方式。本指南深入探讨了CREATE2的复杂性,解释并展示了其实际应用案例。

通过本指南的学习,你将深入理解如何利用这一特性,并将其纳入你的以太坊开发者知识库。

你需要准备什么

  • Ethereum智能合约 有基本理解
  • 安装 Node.js
  • 安装 Hardhat
  • 拥有一个 MetaMask 钱包
  • 在Sepolia测试网上获取一些测试网络ETH(你可以在 这里 获取)
  • 一个基于浏览器的浏览器(例如Chrome)
依赖 版本
Node.js 18.18.0
npm 7≥
@nomicfoundation/hardhat-toolbox ^3.0.0
@openzeppelin/contracts ^4.9.3
dotenv ^16.3.1

你将做什么

  • 了解CREATE2
  • 深入不同的用例
  • 实现CREATE2以预测智能合约地址

什么是CREATE2?

目前,每当部署一个智能合约时,它会收到一个无法在部署前预先确定的随机地址。CREATE2,更中的技术名称是 Skinny CREATE2,是一种操作码,允许开发者在部署之前预先确定智能合约的地址。它是以太坊的 Constantinople升级 的一部分引入的。

值得注意的是,技术上可以通过 nonce 方法来预先确定一个地址。这有一些缺点,例如跟踪nonce(以确保你传入一个有效的值)和必须生成 N 个私钥,以及资助一个不打算部署的地址的脆弱性,但我们将在本指南中不讨论这种方法。

CREATE2的核心组件由一个 常量 值、部署者地址salt(任意值)和 字节码(编译的solidity代码)组成。其公式如下:

address = hash(0xFF, sender, salt, bytecode)

注意

请注意,CREATE2仍然容易受到前置运行攻击。为了应对这一点,开发者可以使用他们的钱包地址(即msg.sender)作为 saltN 部分,以确保它是唯一的且无法伪造。

让我们继续深入研究。

此外,CREATE2启用的一些用例包括:

  • 允许各方之间的离链交易。仅在出现争议时在线部署合约,从而节省成本并提高效率。
  • 设置智能合约,但只有在需要时才进行部署(并支付费用),实现资源的有效使用。
  • 提供服务,例如为用户预配置智能合约,而不必将其在线部署。只有当用户活跃时,智能合约才会被部署,从而减少初始成本。
  • 通过确保在将来的交易中将部署某些合约,从而使DeFi中的复杂操作可预测。

现在我们对CREATE2及其用例有了基本理解。接下来,让我们进行开发环境的设置,然后进行编码!

开发者设置

创建QuickNode端点

为了与以太坊网络进行通信,我们需要访问一个RPC端点。我们不打算自己运行节点并管理其基础设施,而是将繁重的工作交给QuickNode。QuickNode提供了高达8x的响应速度。你可以在 这里 创建一个免费账户。

登录后,选择 创建一个端点 并选择 Ethereum Sepolia Testnet 区块链。创建后,复制 HTTP Provider URL 并保留以备后用,因为你将在后续的部分中需要它。

QuickNode Endpoint

Hardhat设置 👷

打开终端窗口,运行以下命令以创建项目文件夹、初始化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

记住保存文件,然后继续到下一部分!

使用CREATE2创建智能合约

现在,我们的开发环境已经设置好,让我们在 contractsscripts 文件夹中设置逻辑。请注意,我们不会向你展示如何部署一个智能合约,但你可以在 这里 查看快速示例。

让我们打开 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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
QuickNode
QuickNode
江湖只有他的大名,没有他的介绍。