SkillAgentSearch skills...

Createx

Factory smart contract to make easier and safer usage of the `CREATE` and `CREATE2` EVM opcodes as well as of `CREATE3`-based (i.e. without an initcode factor) contract creations.

Install / Use

/learn @pcaversaccio/Createx
About this skill

Quality Score

0/100

Category

Operations

Supported Platforms

Universal

README

CreateX – A Trustless, Universal Contract Deployer <!-- omit from toc -->

🕵️‍♂️ Test CreateX Test Coverage 👮‍♂️ Sanity checks 🚀 UI deployment License: AGPL-3.0-only

<img src=https://github-production-user-asset-6210df.s3.amazonaws.com/25297591/272914952-38a5989c-0113-427d-9158-47646971b7d8.png width="1050"/>

Factory smart contract to make easier and safer usage of the CREATE and CREATE2 EVM opcodes as well as of CREATE3-based (i.e. without an initcode factor) contract creations.

[!NOTE] The CreateX contract should be considered as maximally extensible. Be encouraged to build on top of it! The Solidity-based interface can be found here.

So What on Earth Is a Contract Factory?

It is important to understand that Ethereum Virtual Machine (EVM) opcodes can only be called via a smart contract. A contract factory in the context of the EVM refers to a special smart contract that is used to create and deploy other smart contracts on EVM-compatible blockchains using contract creation opcodes (i.e. CREATE or CREATE2). Using a contract factory provides a flexible and efficient way to deploy and manage smart contracts that share similar functionalities but may have different configurations or settings.

Different approaches can be used to create contracts using a factory contract, and this is exactly what CreateX offers: a comprehensive range of contract creation functions that are triggered by a smart contract itself. It is worth emphasising the two differences in the address calculation of the opcodes CREATE and CREATE2 (|| stands for byte-wise concatenation, [12:] refers to the last 20 bytes of a 32-byte expression, and rlp is an abbreviation for Ethereum's "Recursive Length Prefix" serialisation scheme):

  • CREATE: address computedAddress = keccak256(rlpEncode([deployerAddress, deployerNonce]))[12:],
  • CREATE2: address computedAddress = keccak256(0xff||deployerAddress||salt||keccak256(initCode))[12:].

Available Versatile Functions

CreateX
├── CREATE
│   ├── Read-Only Functions
│   │   ├── "function computeCreateAddress(uint256) view returns (address)"
│   │   └── "function computeCreateAddress(address,uint256) view returns (address)"
│   └── Write Functions
│       ├── "function deployCreate(bytes) payable returns (address)"
│       ├── "function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
│       ├── "function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
│       └── "function deployCreateClone(address,bytes) payable returns (address)"
├── CREATE2
│   ├── Read-Only Functions
│   │   ├── "function computeCreate2Address(bytes32,bytes32) view returns (address)"
│   │   └── "function computeCreate2Address(bytes32,bytes32,address) pure returns (address)"
│   └── Write Functions
│       ├── "function deployCreate2(bytes) payable returns (address)"
│       ├── "function deployCreate2(bytes32,bytes) payable returns (address)"
│       ├── "function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
│       ├── "function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
│       ├── "function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
│       ├── "function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
│       ├── "function deployCreate2Clone(address,bytes) payable returns (address)"
│       └── "function deployCreate2Clone(bytes32,address,bytes) payable returns (address)"
└── CREATE3
    ├── Read-Only Functions
    │   ├── "function computeCreate3Address(bytes32) view returns (address)"
    │   └── "function computeCreate3Address(bytes32,address) pure returns (address)"
    └── Write Functions
        ├── "function deployCreate3(bytes) payable returns (address)"
        ├── "function deployCreate3(bytes32,bytes) payable returns (address)"
        ├── "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
        ├── "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)"
        ├── "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
        └── "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)"
<details> <summary> <a href="https://github.com/pcaversaccio/createx/blob/main/src/CreateX.sol#L302-L317"><code>computeCreateAddress(uint256)</code></a> </summary>

Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX) using the CREATE opcode. For the specification of the Recursive Length Prefix (RLP) encoding scheme, please refer to p. 20 of the Ethereum Yellow Paper and the Ethereum Wiki. Based on the EIP-161 specification, all contract accounts on the Ethereum mainnet are initiated with nonce = 1. Thus, the first contract address created by another contract is calculated with a non-zero nonce.

# /*:°• Function Argument •°:*/ #
- name: nonce
  type: uint256
  description: The next 32-byte nonce of this contract.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
</details> <details> <summary> <a href="https://github.com/pcaversaccio/createx/blob/main/src/CreateX.sol#L242-L300"><code>computeCreateAddress(address,uint256)</code></a> </summary>

Returns the address where a contract will be stored if deployed via deployer using the CREATE opcode. For the specification of the Recursive Length Prefix (RLP) encoding scheme, please refer to p. 20 of the Ethereum Yellow Paper and the Ethereum Wiki. Based on the EIP-161 specification, all contract accounts on the Ethereum mainnet are initiated with nonce = 1. Thus, the first contract address created by another contract is calculated with a non-zero nonce.

# /*:°• Function Arguments •°:*/ #
- name: deployer
  type: address
  description: The 20-byte deployer address.
- name: nonce
  type: uint256
  description: The next 32-byte nonce of the deployer address.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
</details> <details> <summary> <a href="https://github.com/pcaversaccio/createx/blob/main/src/CreateX.sol#L122-L136"><code>deployCreate(bytes)</code></a> </summary>

Deploys a new contract via calling the CREATE opcode and using the creation bytecode initCode and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if msg.value is non-zero, initCode must have a payable constructor.

# /*:°• Function Argument •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.

# /*:°• Return Value •°:*/ #
- name: newContract
  type: address
  description: The 20-byte address where the contract was deployed.
</details>

<detail

View on GitHub
GitHub Stars550
CategoryOperations
Updated17h ago
Forks71

Languages

Solidity

Security Score

100/100

Audited on Apr 2, 2026

No findings