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

2465

被折叠的 条评论
为什么被折叠?



