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/CreatexREADME
CreateX – A Trustless, Universal Contract Deployer <!-- omit from toc -->
<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
CreateXcontract 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?
- Available Versatile Functions
- Special Features
- Design Principles
- Security Considerations
- Tests
- ABI (Application Binary Interface)
- New Deployment(s)
CreateXDeployments- Integration With External Tooling
- Community-Maintained Dune Dashboards
- 🙏🏼 Acknowledgement
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
