使用CREATE2在不同链部署地址相同的合约

CREATE2

在合约部署时,通常情况下会返回一个随机地址,即使是同一个合约多次部署地址也不相同,这就在想使用同一地址部署时遇到麻烦,这是以太坊推出了CREATE2功能来解决这个问题。
CREATE2能够让我们预测合约部署的地址,此操作码背后的整个想法是使生成的地址独立于未来事件。无论区块链上发生什么,始终可以在预先计算的地址上部署合约。

新地址取决于:
  • 一个常量值
  • 部署者地址
  • 盐值(任意值)
  • 字节码(编译后的合约文件)
其新合约的计算方式为:
address = hash(0xFF, sender, salt, bytecode)

通过这个公式我们可以计算出要创建的合约的地址,从而去满足条件创建另外一个地址相同的合约。

代码示例

本示例展示的是在ETH测试网sepolia和BSC测试网上部署一个相同地址的合约

提前准备
  • 创建一个公私钥,在这个新的公私钥中,可以转入代币,但不能转出代币,原因是执行转出操作时,会改变该账号在相应链上的nonce值,从而导致生成地址不一致问题。
  • 通过测试网水龙头向该账号中转入测试币,这里需要测试币是因为部署合约时需要支付一定的币
  • 创建ETHSCAN上的apikey,bsc的apikey与eth相同,创建方式可以自行搜索,个密钥主要用于在部署合约后,通过 Hardhat 的验证插件自动验证并公开您的合约源代码。
  • 测试网地址,用于连接到测试网
项目初始化

这里我们使用hardhat来创建我们的开发环境,hardhat支持编译、部署、测试和调试智能合约,还是比较方便的。

  • 创建目录
    mkdir create2-factory
    cd create2-factory
    
  • 初始化目录
    npm init -y
    
  • 安装hardhat环境
    npm install --save-dev hardhat
    
  • 初始化hardhat项目,快速创建一个 Hardhat 项目结构,包括配置文件、示例合约和测试脚本。
    npx hardhat
    

这些执行完后会得到以下目录结构
目录结构

工厂合约编写

工厂合约允许你在部署前就知道合约地址,并能保证在多个链上部署出相同地址的合约。也就是通过工厂合约来预测你部署的合约在链上的地址。
工厂合约主要做两件事情

  • 使用 CREATE2 部署目标合约
  • 可预先计算出目标地址,确保两条链部署结果一致
工厂合约相关代码

我们在contracts目录下创建Factory.sol文件

// SPDX-License-Identifier: MIT

// 工厂合约
pragma solidity ^0.8.28;

contract Factory {
    event Deployed(address addr, bytes32 salt);

    function deploy(bytes memory bytecode, bytes32 salt) public {
        address addr;
        assembly {
            addr := create2(
                0, // 发送的以太币数量
                add(bytecode, 0x20), // 合约字节码的起始位置
                mload(bytecode),     // 合约字节码的长度
                salt                 // 盐值
            )
            if iszero(extcodesize(addr)) {
                revert(0, 0)
            }
        }
        emit Deployed(addr, salt);
    }

    function computeAddress(bytes32 salt, bytes memory bytecode) public view returns (address) {
        bytes32 bytecodeHash = keccak256(bytecode);
        return address(uint160(uint256(keccak256(abi.encodePacked(
            bytes1(0xff),
            address(this),
            salt,
            bytecodeHash
        )))));
    }
}
  • deploy函数用于使用 CREATE2 部署目标合约
  • computeAddress用于预测目标地址,确保两条链部署结果一致
测试合约编写

测试合约比较随意,主要作用是部署到链上后测试链上地址是否相同

测试合约相关代码

首先在contracts目录下创建一个SimpleContract.sol文件

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract SimpleContract {
    uint256 public value;

    constructor(uint256 _value) {
        value = _value;
    }
}

主要功能是存储一个在部署时指定的 uint256 类型的数值,并允许外部访问该数值。构造函数确保了在合约部署时对状态变量 value 进行初始化。

编写hardhat.config.js文件

​hardhat.config.js 是 Hardhat 项目的核心配置文件,用于定义项目的编译器版本、网络连接、插件集成、路径设置等关键参数。
这里我们使用该文件主要配置连接链相关的地址

require("@nomicfoundation/hardhat-ethers");

// 从 Alchemy 获取的 API 密钥,用于连接以太坊网络。
const ALCHEMY_API_KEY = "这个密钥是我用于连接测试网时使用的,可有可无";
// 从 Etherscan 获取的 API 密钥,用于验证以太坊合约。
const ETHERSCAN_API_KEY = "ETH浏览器上面申请的apikey";
const PRIVATE_KEY = "账户私钥";

module.exports = {
  solidity: "0.8.28",
  networks: {
    sepolia: {
      url: "以太坊测试网地址",
      accounts: [PRIVATE_KEY],
    },
    bscTestnet: {
      url: "https://data-seed-prebsc-1-s1.binance.org:8545/",
      accounts: [PRIVATE_KEY],
    },
  },
  etherscan: {
    apiKey: {
      sepolia: ETHERSCAN_API_KEY,
      bscTestnet: ETHERSCAN_API_KEY,
    },
  },
};
编译合约
npx hardhat compile

在编译完成后会有artifacts目录,在这个目录下放的是合约ABI相关的一些文件,感兴趣可以自行了解。

写自动化部署程序

这里是有两个,我们放在scripts目录下,没有可以自行创建,没有明确要求

工厂合约部署程序

在scripts目录下创建factory.js文件

const hre = require("hardhat");

async function main() {
    const Factory = await hre.ethers.getContractFactory("你的工厂合约名称,如果你是按照上面代码写的合约,你应该写Factory");
    const factory = await Factory.deploy();
    await factory.waitForDeployment();
    console.log("Factory 合约已部署到:", await factory.getAddress());
}

main().catch((error) => {
    console.error(error);
    process.exit(1);
});
你的合约部署文件

在scripts目录下创建deploy.js文件

const hre = require("hardhat");
const { ethers } = hre;

async function main() {
    const factoryAddress = "这个是部署完工程合约后,工厂合约的地址";

    const Factory = await ethers.getContractFactory("Factory");
    const factory = Factory.attach(factoryAddress);

    const SimpleContract = await ethers.getContractFactory("SimpleContract");
    const bytecode = SimpleContract.bytecode;

    const salt = ethers.encodeBytes32String("这个随意填写,相当于你的密钥");

    const tx = await factory.waitForDeployment(bytecode, salt);
    await tx.waitForDeployment();

    const deployedAddress = await factory.computeAddress(salt, bytecode);
    console.log("SimpleContract 合约已部署到:", deployedAddress);
}

main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });
部署合约

前面准备工作做好后我们就可以进行合约部署,要先部署工厂合约,然后在部署你自己的合约。顺序不能乱

  1. 部署工厂合约
    在部署过程中,比如你要在ETH和BSC链上部署,那就把下面的命令执行两次。
    npx hardhat run <你的工厂合约部署程序路径,也就是factory.js文件的路径> --network <网络,在hardhat.config.js中配置的链的地址,你可以使用在该文件中配置的别名,例如要在sepolia上部署那就是sepolia>
    
    在执行完命令后会返回一个工厂合约地址,正常情况下两次执行结果应该是一致的,不一致可能是你的账号出现了问题。
  2. 部署你自己的合约
    上面部署完工厂合约后,把工厂合约地址填入到deploy.js中,对应这段代码
    const factoryAddress = "这个是部署完工程合约后,工厂合约的地址";
    
    在部署过程中,比如你要在ETH和BSC链上部署,那就把下面的命令执行两次。
    npx hardhat run <你的合约部署程序路径,也就是factory.js文件的路径> --network <网络,在hardhat.config.js中配置的链的地址,你可以使用在该文件中配置的别名,例如要在sepolia上部署那就是sepolia>
    
    在执行完后就会拿到相对应的你的合约部署地址,如果不一致,请检查一下你的私钥及盐值。

这样你就在两个链上部署了地址相同的合约。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值