一文详解solidity中的CREATE2操作码

  • Louis
  • 更新于 2024-08-28 17:27
  • 阅读 1393

CREATE2是Solidity中的一个操作码,用于创建新的智能合约。它是在以太坊的君士坦丁堡硬分叉中引入的。

基本概念:

CREATE2 是 Solidity 中的一个操作码,用于创建新的智能合约。它是在以太坊的君士坦丁堡硬分叉中引入的。

在介绍CREATE2之前,我们先来回顾下CREATE操作码的工作原理:

CREATE操作码是 Solidity 中最常见的合约创建方式。合约地址由发起交易的账户地址和该账户的交易次数(nonce)共同决定。合约的地址是通过哈希计算(keccak256)生成的。

地址生成方式:

合约地址 = keccak256(rlp(sender_address, nonce))

特点:

  • 每次创建新合约时,合约地址都会不同,因为交易次数 (nonce) 会不断增加。
  • 无法在合约创建前预测生成的合约地址。

CREATE2主要特性:

1、确定性地址: 与标准 CREATE 操作码不同,CREATE2 允许您在实际部署新合约之前计算新合约的地址。

2、基于“加盐”操作: 它使用“盐”(随机值)作为地址计算的一部分,使开发人员可以更好地控制结果地址。

3、可预测的部署: 相同的初始化代码和盐将始终产生相同的地址,无论何时何地部署。

工作原理:

使用 CREATE2 部署的合约的地址计算如下:

address = keccak256(0xff ++ senderAddress ++ salt ++ keccak256(initCode))[12:]

其中:

  • 0xff: 是常量字节,如果表示成10进制就是255。
  • senderAddress:是创建新合约的合约地址。
  • salt:创建者选择的32字节的值
  • initCode:是待创建合约的初始化代码

在solidity中如何使用:

我们看如下代码:

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

contract SimpleContract {
  uint256 public value;

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

contract Factory {
  event Deployed(address addr);

  function deploy(bytes32 salt, uint256 _value) public returns (address) {
    // 合约的字节码
    bytes memory bytecode = abi.encodePacked(
      type(SimpleContract).creationCode,
      abi.encode(_value)
    );

    address addr;
    assembly {
      // 使用 CREATE2 操作码部署合约
      addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
      // 检查合约是否部署成功
      if iszero(extcodesize(addr)) {
        revert(0, 0)
      }
    }

    emit Deployed(addr);
    return addr;
  }

  function computeAddress(bytes32 salt, bytes32 bytecodeHash) public view returns (address) {
    return address(uint160(uint256(keccak256(abi.encodePacked(
      bytes1(0xff),
      address(this),
      salt,
      bytecodeHash
    )))));
  }

  function getBytecodeHash(uint256 _value) public pure returns (bytes32) {
    bytes memory bytecode = abi.encodePacked(
      type(SimpleContract).creationCode,
      abi.encode(_value)
    );
    return keccak256(bytecode);
  }
}

代码解释:

SimpleContract: 一个简单的合约,带有一个状态变量 value 和一个构造函数用于初始化这个变量。

Factory: 工厂合约,包含以下几个函数:

deploy: 使用 CREATE2 操作码部署 SimpleContract 合约。

  • salt: 用于生成确定性地址的盐值。
  • _value: 传递给 SimpleContract 构造函数的参数。
  • 使用 assembly 代码调用 CREATE2 操作码,部署合约并检查部署是否成功。

computeAddress: 计算给定盐值和字节码哈希的合约地址。

  • salt: 用于生成确定性地址的盐值。
  • bytecodeHash: 部署合约字节码的哈希值。

getBytecodeHash: 计算 SimpleContract 合约字节码的哈希值。

  • _value: 传递给 SimpleContract 构造函数的参数。

部署和使用步骤

1、部署 Factory 合约: 部署 Factory 合约到以太坊网络上。

2、计算 SimpleContract 合约字节码的哈希值:调用 getBytecodeHash 函数,传递 _value 参数,获取 SimpleContract 的字节码哈希值。

3、计算合约地址: 调用 computeAddress 函数,传递 salt 和前一步获取的 bytecodeHash,计算将要部署的 SimpleContract 合约的地址。

4、部署 SimpleContract 合约: 调用 deploy 函数,传递 salt_value 参数,使用 CREATE2 操作码部署 SimpleContract 合约。

这里有一个细节需要注意:salt需要传递一个bytes32的类型,如果你输入一个int类型,需要转换一下。

通过上述步骤,可以使用 CREATE2 操作码在部署之前计算合约地址,并在部署时确保合约被部署到预期地址。

CREATE2 操作码具体应用的业务场景

在去中心化金融(DeFi)以及其他智能合约应用中,create2 的特性允许开发者提前预测和控制新合约的地址。这种功能在某些场景中非常有用,尤其是当合约地址的确定性对整个系统的运作至关重要时。以下是更详细的解释和例子:

1. 去中心化交易所的流动性池

在去中心化交易所(如 Uniswap)中,每个交易对都会有一个独立的流动性池合约,这些合约的地址由交易对的两个代币决定。如果使用 create2 部署流动性池合约,开发者可以确保对于给定的代币对(例如 ETH/USDT),流动性池的合约地址在创建前就可以被确定。这使得以下场景成为可能:

  • 预先计算和确认流动性池地址: 用户或其他合约可以在交易对的流动性池实际部署前计算出该池的地址,从而提前与其互动或做出决策(如锁定资产、设置权限)。
  • 流动性池的重建: 如果某个流动性池合约因为某种原因被销毁,使用相同的 salt 和初始化代码,流动性池可以在相同地址重新部署,保证系统的一致性。

2. 分布式应用的模块化设计

在某些分布式应用(DApps)中,可能会有多个模块化合约,这些合约之间通过地址直接交互。create2 可以确保这些模块的合约地址在开发阶段就已知,从而简化合约之间的依赖管理。例如:

  • 模块间依赖: 如果一个模块的合约需要调用另一个模块的特定合约功能,在使用 create2 部署合约的情况下,调用方合约可以在开发时就知道被调用合约的地址,从而避免在运行时传递不必要的参数。
  • 升级机制: 如果某个模块需要升级,只要使用相同的 salt 部署新版本的合约,旧的模块地址可以指向新部署的合约,从而简化了升级过程。

3. 预言机系统

在某些情况下,预言机系统需要与合约进行通信,并确保结果被发送到一个确定的地址。使用 create2,可以在预言机系统中提前设定目标合约的地址:

  • 可信数据推送: 预言机可以在链下计算并确认目标合约的地址,确保推送的数据到达正确的接收合约,而不会因为地址变化导致误发送。

4. 钱包和智能合约账户

在一些钱包或智能合约账户的实现中,用户的账户地址可能是基于某些参数(如用户的公钥)生成的。create2 允许在部署这些账户合约之前,预先计算出这些地址:

  • 账户地址的确定性: 用户可以知道他们的智能合约钱包地址在特定参数下是固定的,即使钱包合约被销毁并重新部署,地址依然保持不变。
  • 助记词恢复: 如果智能合约钱包的地址是基于用户的助记词或其他确定性输入生成的,create2 允许用户通过相同的输入恢复他们的钱包。

5. 多方签名和托管合约

在多方签名(multi-signature)和托管合约(custodial contracts)的应用中,使用 create2 可以确保合约的地址是预先确定的,从而在合约实际部署之前就可以依赖它进行多方协调:

  • 多方签名协议: 多方在协议初期就可以商定并确定未来多签合约的地址,这样可以在合约部署前进行签名、资产锁定等操作。
  • 托管合约的透明性: 在资产托管系统中,托管合约的地址可以提前确定,资产可以提前转移到预定地址,而不会受到合约部署顺序的影响。

总结

create2 的可预测性使得开发者能够在合约部署之前确定合约地址,这在 DeFi 和智能合约系统中具有重要意义。它使得系统设计更为灵活和透明,尤其在需要地址确定性的场景下,create2 提供了强大的工具来管理合约的部署和交互。

点赞 1
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Louis
Louis
web3 developer,技术交流或者有工作机会可加VX: magicalLouis