当前位置:   article > 正文

Solidity——create和create2部署合约_合约create 合约地址

合约create 合约地址

部署智能合约是开发中必不可少的一个环节,常规的方式是借助像Hardhat这样的工具,通过编写ts部署脚本来实现。但在实际业务中,经常会遇到通过在合约中部署子合约的情况。比如添加Token流动池。这类需求在设计在上需要通过工厂合约来创建部署子合约来实现,它看起来就像是从一个模具厂生产的模具一样,只是每个模具的编号(子合约地址)不同。子合约的业务逻辑不是本次介绍的重点,我们主要关注在合约中部署合约的两种方式:

一、create部署

先确认子合约的内容,一个构造函数和简单的函数:

  1. contract Son {
  2. address token1;
  3. address token2;
  4. constructor(address _token1,address _token2) {
  5. token1 = _token1;
  6. token2 = _token2;
  7. }
  8. function addLiquidity() external pure returns(uint256){
  9. return 1;
  10. }
  11. }

create的部署方式是通过new关键字来实现,所部署合约的地址是通过哈希计算得来: 

  1. 部署地址 —msg.sender
  2. 之前在该地址部署的交易数  — nonce

keccak256(rlp.encode(deployingAddress, nonce))

nonce每次获取的不一样,因此每次部署的合约地址不同。在solidity最新的版本中,只需通过内置的关键字new即可:

X x = new X()

  1. //新版本的create部署方式
  2. //0x4D1435CA4761C96ea6156AFf63A9F0D8D00D10c1
  3. //0xFF8C88bA69cbfba47BfC1ff84aB2cfA6a64B8429
  4. function create(address token1,address token2) external returns(address){
  5. Son son = new Son(token1,token2);
  6. return address(son);
  7. }

remix运行结果:

合约地址:0x5C9eb5D6a6C2c1B3EFc52255C0b356f116f6f66D,我们用相同的参数token1、token2,再次部署的结果: 

合约地址: 0xb8f43EC36718ecCb339B75B727736ba14F174d77,可以看到与第一次的地址不同,create部署的好处是每次的部署地址不同,不用担心合约地址被占用导致部署失败的问题,缺点是无法通过计算得到合约地址,不适合一些需要提前知道合约地址的场景。

二、create2部署

合约地址计算的方式:

  1. 部署地址(msg.sender)
  2. 部署合约代码字节码的哈希(bytecode)
  3. 创建者提供的随机的salt (32 字节字符串).

keccak256(0xff ++ deployingAddr ++ salt ++ keccak256(bytecode))

相较于create,多了参数salt。

  1. //新版本的create2部署方式
  2. function create2(address token1,address token2) external returns(address){
  3. bytes32 salt = keccak256(abi.encodePacked(token1, token2));//盐值,可以是一个数字、一个字符串等,一般是随机数
  4. Son son = new Son{salt: salt}(token1,token2);
  5. return address(son);
  6. }

salt的获取规则是计算token1和token2的hash,也可以根据实际的业务进行调整。部署代码:

X x = new X{salt: salt}()

token1和token2仍然使用create中的参数值,remix运行结果: 

合约地址为:0x8D0Cd60156182DF2263a41960c250Bd921047Bc3 ,与create部署的合约地址不同,因为底层的计算方式不同。现在我们用相同的参数再次部署:

已经报错,因为相同的salt计算的合约地址是相同的,而合约地址必须未被使用过。

我们用旧版本的create2来试试看

  1. //老版本的create2部署方式,通过solidity汇编语法实现
  2. function deployAssembly(address token1,address token2) external returns(address addr){
  3. bytes memory bytecode = getBytecode(token1, token2);
  4. bytes32 salt = keccak256(abi.encodePacked(token1, token2));//盐值,可以是一个数字、一个字符串等,一般是随机数
  5. assembly {
  6. //param1:发送给新合约的wei数(msg.value)
  7. //param2:存储位置(从32开始)和需要的长度(bytecode)
  8. //param3:存储在memory中
  9. //param4:随机值
  10. addr := create2(0,add(bytecode, 32),mload(bytecode),salt)
  11. if iszero(extcodesize(addr)) {
  12. revert(0, 0)
  13. }
  14. }
  15. }
  16. function getBytecode(address token1,address token2) internal pure returns(bytes memory bytecode){
  17. bytecode = type(Son).creationCode;//获取合约字节码,也可通过编译合约获取
  18. bytecode = abi.encodePacked(bytecode, abi.encode(token1, token2));
  19. }

需要提前通过creationCode()函数获取部署子合约的bytecode。部署结果:

可以看到结果仍然是失败,因为与上述计算的合约地址相同,只是实现方式不同。我们通过计算合约地址的函数来验证是否与部署成功的地址一致:

可以看到计算结果是:0x8D0Cd60156182DF2263a41960c250Bd921047Bc3,与create2函数返回的合约地址相同。

相较于create,create2提供了更多的灵活性,使得开发者可以预先计算合约地址,从而更好地管理合约地址的分配。同时,create2还可以用于实现一些更高级的功能,例如合约工厂、二级合约等。

需要注意的是,使用create2创建合约时,由于需要提供一个预计地址,因此需要确保该地址没有被使用过,否则可能会导致创建失败。此外,使用create2创建合约时,需要确保预计地址的计算规则是确定的,这样可以确保预计地址与实际地址的一致性。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/542886
推荐阅读
相关标签
  

闽ICP备14008679号