Giter VIP home page Giter VIP logo

pcaversaccio / createx Goto Github PK

View Code? Open in Web Editor NEW
286.0 6.0 15.0 2.68 MB

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.

Home Page: https://createx.rocks

License: GNU Affero General Public License v3.0

Solidity 80.81% JavaScript 0.46% TypeScript 17.91% CSS 0.82%
create create2 create3 deployment factory-contract solidity ethereum

createx's Introduction

CreateX – A Trustless, Universal Contract Deployer

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

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)"
computeCreateAddress(uint256)

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. 19 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.
computeCreateAddress(address,uint256)

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. 19 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.
deployCreate(bytes)

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.
deployCreateAndInit(bytes,bytes,tuple(uint256,uint256))

Deploys and initialises a new contract via calling the CREATE opcode and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor, and any excess ether is returned to msg.sender.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address)

Deploys and initialises a new contract via calling the CREATE opcode and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, the refund address refundAddress, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
  type: address
  description: The 20-byte address where any excess ether is returned to.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreateClone(address,bytes)

Deploys a new EIP-1167 minimal proxy contract using the CREATE opcode, and initialises the implementation contract using the implementation address implementation, the initialisation code data, and msg.value as inputs. Note that if msg.value is non-zero, the initialiser function called via data must be payable.

# /*:°• Function Arguments •°:*/ #
- name: implementation
  type: address
  description: The 20-byte implementation contract address.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed proxy contract.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

computeCreate2Address(bytes32,bytes32)

Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX) using the CREATE2 opcode. Any change in the initCodeHash or salt values will result in a new destination address.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the contract address.
- name: initCodeHash
  type: bytes32
  description: The 32-byte bytecode digest of the contract creation bytecode.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
computeCreate2Address(bytes32,bytes32,address)

Returns the address where a contract will be stored if deployed via deployer using the CREATE2 opcode. Any change in the initCodeHash or salt values will result in a new destination address.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the contract address.
- name: initCodeHash
  type: bytes32
  description: The 32-byte bytecode digest of the contract creation bytecode.
- name: deployer
  type: address
  description: The 20-byte deployer address.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
deployCreate2(bytes)

Deploys a new contract via calling the CREATE2 opcode and using the creation bytecode initCode and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! 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.
deployCreate2(bytes32,bytes)

Deploys a new contract via calling the CREATE2 opcode and using the salt value salt, 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 Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.

# /*:°• Return Value •°:*/ #
- name: newContract
  type: address
  description: The 20-byte address where the contract was deployed.
deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256))

Deploys and initialises a new contract via calling the CREATE2 opcode and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor, and any excess ether is returned to msg.sender.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256))

Deploys and initialises a new contract via calling the CREATE2 opcode and using the salt value salt, creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor, and any excess ether is returned to msg.sender.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address)

Deploys and initialises a new contract via calling the CREATE2 opcode and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, the refund address refundAddress, and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
  type: address
  description: The 20-byte address where any excess ether is returned to.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address)

Deploys and initialises a new contract via calling the CREATE2 opcode and using the salt value salt, the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, the refund address refundAddress, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
  type: address
  description: The 20-byte address where any excess ether is returned to.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate2Clone(address,bytes)

Deploys a new EIP-1167 minimal proxy contract using the CREATE2 opcode and the salt value salt, and initialises the implementation contract using the implementation address implementation, the initialisation code data, and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! Note that if msg.value is non-zero, the initialiser function called via data must be payable.

# /*:°• Function Arguments •°:*/ #
- name: implementation
  type: address
  description: The 20-byte implementation contract address.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed proxy contract.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate2Clone(bytes32,address,bytes)

Deploys a new EIP-1167 minimal proxy contract using the CREATE2 opcode and the salt value salt, and initialises the implementation contract using the implementation address implementation, the initialisation code data, and msg.value as inputs. Note that if msg.value is non-zero, the initialiser function called via data must be payable.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.
- name: implementation
  type: address
  description: The 20-byte implementation contract address.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed proxy contract.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

computeCreate3Address(bytes32)

Returns the address where a contract will be stored if deployed via this contract (i.e. CreateX) using the CREATE3 pattern (i.e. without an initcode factor). Any change in the salt value will result in a new destination address.

# /*:°• Function Argument •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
computeCreate3Address(bytes32,address)

Returns the address where a contract will be stored if deployed via deployer using the CREATE3 pattern (i.e. without an initcode factor). Any change in the salt value will result in a new destination address.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.
- name: deployer
  type: address
  description: The 20-byte deployer address.

# /*:°• Return Value •°:*/ #
- name: computedAddress
  type: address
  description: The 20-byte address where a contract will be stored.
deployCreate3(bytes)

Deploys a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the salt value salt, the creation bytecode initCode, and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! 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.
deployCreate3(bytes32,bytes)

Deploys a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the salt value salt, 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 Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.

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

ℹ️ Note
We strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal to msg.sender in the salt to prevent maliciously intended frontrun proxy deployments on other chains.

deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256))

Deploys and initialises a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor, and any excess ether is returned to msg.sender.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256))

Deploys and initialises a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the salt value salt, the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor, and any excess ether is returned to msg.sender.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system. Furthermore, we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal to msg.sender in the salt to prevent maliciously intended frontrun proxy deployments on other chains.

deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address)

Deploys and initialises a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, the refund address refundAddress, and msg.value as inputs. The salt value is calculated pseudo-randomly using a diverse selection of block and transaction properties. This approach does not guarantee true randomness! In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor.

# /*:°• Function Arguments •°:*/ #
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
  type: address
  description: The 20-byte address where any excess ether is returned to.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address)

Deploys and initialises a new contract via employing the CREATE3 pattern (i.e. without an initcode factor) and using the salt value salt, the creation bytecode initCode, the initialisation code data, the struct for the payable amounts values, the refund address refundAddress, and msg.value as inputs. In order to save deployment costs, we do not sanity check the initCode length. Note that if values.constructorAmount is non-zero, initCode must have a payable constructor.

# /*:°• Function Arguments •°:*/ #
- name: salt
  type: bytes32
  description: The 32-byte random value used to create the proxy contract address.
- name: initCode
  type: bytes
  description: The creation bytecode.
- name: data
  type: bytes
  description: The initialisation code that is passed to the deployed contract.
- name: values
  type: tuple(uint256,uint256)
  description: The specific `payable` amounts for the deployment and initialisation call.
- name: refundAddress
  type: address
  description: The 20-byte address where any excess ether is returned to.

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

ℹ️ Note
This function allows for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system. Furthermore, we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal to msg.sender in the salt to prevent maliciously intended frontrun proxy deployments on other chains.

Special Features

Tip

Note that the separate project createXcrunch is a Rust-based program designed to efficiently find zero-leading, zero-containing, or pattern-matching deployment addresses, taking into account the subsequent special features of CreateX.

Permissioned Deploy Protection and Cross-Chain Redeploy Protection

The salt value implements different safeguarding mechanisms depending on the encoded values in the salt (|| stands for byte-wise concatenation):

=> salt (32 bytes) = 0xbebebebebebebebebebebebebebebebebebebebe||ff||1212121212121212121212
  • The first 20 bytes (i.e. bebebebebebebebebebebebebebebebebebebebe) may be used to implement a permissioned deploy protection by setting them equal to msg.sender,
  • The 21st byte (i.e. ff) may be used to implement a cross-chain redeploy protection by setting it equal to 0x01,
  • The last random 11 bytes (i.e. 1212121212121212121212) allow for $2^{88}$ bits of entropy for mining a salt.

The full logic is implemented in the internal _guard function:

function _guard(bytes32 salt) internal view returns (bytes32 guardedSalt) {
  (
    SenderBytes senderBytes,
    RedeployProtectionFlag redeployProtectionFlag
  ) = _parseSalt({ salt: salt });

  if (
    senderBytes == SenderBytes.MsgSender &&
    redeployProtectionFlag == RedeployProtectionFlag.True
  ) {
    // Configures a permissioned deploy protection as well as a cross-chain redeploy protection.
    guardedSalt = keccak256(abi.encode(msg.sender, block.chainid, salt));
  } else if (
    senderBytes == SenderBytes.MsgSender &&
    redeployProtectionFlag == RedeployProtectionFlag.False
  ) {
    // Configures solely a permissioned deploy protection.
    guardedSalt = _efficientHash({
      a: bytes32(uint256(uint160(msg.sender))),
      b: salt
    });
  } else if (senderBytes == SenderBytes.MsgSender) {
    // Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
    revert InvalidSalt({ emitter: _SELF });
  } else if (
    senderBytes == SenderBytes.ZeroAddress &&
    redeployProtectionFlag == RedeployProtectionFlag.True
  ) {
    // Configures solely a cross-chain redeploy protection. In order to prevent a pseudo-randomly
    // generated cross-chain redeploy protection, we enforce the zero address check for the first 20 bytes.
    guardedSalt = _efficientHash({ a: bytes32(block.chainid), b: salt });
  } else if (
    senderBytes == SenderBytes.ZeroAddress &&
    redeployProtectionFlag == RedeployProtectionFlag.Unspecified
  ) {
    // Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
    revert InvalidSalt({ emitter: _SELF });
  } else {
    // For the non-pseudo-random cases, the salt value `salt` is hashed to prevent the safeguard mechanisms
    // from being bypassed. Otherwise, the salt value `salt` is not modified.
    guardedSalt = (salt != _generateSalt())
      ? keccak256(abi.encode(salt))
      : salt;
  }
}

Please note that when you configure a permissioned deploy protection, you must specify whether you want cross-chain redeploy protection (i.e. 21st byte equals 0x01) or not (i.e. the 21st byte equals 0x00). The underlying reason for this logic is to enforce developer explicitness. If you don't specify a cross-chain redeploy protection decision (i.e. the 21st byte is greater than 0x01) the function reverts.

Furthermore, you can configure only cross-chain redeploy protection by setting the first 20 bytes equal to the zero address 0x0000000000000000000000000000000000000000. The rationale behind this logic is to prevent a pseudo-randomly generated 32 byte salt from inadvertently activating cross-chain redeploy protection. Also in this case, if you don't specify a cross-chain redeploy protection, i.e. the 21st byte is greater than 0x01, the function reverts. The underlying reason for this logic is as well to enforce developer explicitness.

Pseudo-Random Salt Value

For developer convenience, the CreateX contract offers several overloaded functions that generate the salt value pseudo-randomly using a diverse selection of block and transaction properties. Please note that this approach does not guarantee true randomness!

The full logic is implemented in the internal _generateSalt function:

function _generateSalt() internal view returns (bytes32 salt) {
  unchecked {
    salt = keccak256(
      abi.encode(
        // We don't use `block.number - 256` (the maximum value on the EVM) to accommodate
        // any chains that may try to reduce the amount of available historical block hashes.
        // We also don't subtract 1 to mitigate any risks arising from consecutive block
        // producers on a PoS chain. Therefore, we use `block.number - 32` as a reasonable
        // compromise, one we expect should work on most chains, which is 1 epoch on Ethereum
        // mainnet. Please note that if you use this function between the genesis block and block
        // number 31, the block property `blockhash` will return zero, but the returned salt value
        // `salt` will still have a non-zero value due to the hashing characteristic and the other
        // remaining properties.
        blockhash(block.number - 32),
        block.coinbase,
        block.number,
        block.timestamp,
        block.prevrandao,
        block.chainid,
        msg.sender
      )
    );
  }
}

Design Principles

  • CreateX should cover most but not all contract creation use cases.
  • CreateX should be human-readable and should be simple to understand for readers with low prior experience.
  • CreateX should be maximally secure, i.e. no hidden footguns.
  • CreateX should be trustless.
  • CreateX should be stateless.
  • CreateX should be extensible (i.e. it can be used to deploy protocols, within protocols, or to deploy other types of deterministic deployer factories).

The following consequences result from these principles:

  • We only use inline assembly if it is required or if the code section itself is based on short and/or audited code.
  • We document the contract to the smallest detail.
  • We extensively fuzz test all functions.
  • We deliberately do not implement special functions for clones with immutable arguments, as there is neither a finalised standard nor a properly audited contract version.
  • We do not implement any special functions for EIP-5202 (a.k.a. blueprint contracts), as all existing functions in CreateX are already cost-effective alternatives in our opinion.

Security Considerations

Warning

This contract is unaudited! Special thanks go to Oleksii Matiiasevych for his thorough review and feedback 🙏🏽.

Generally, for security issues, see our Security Policy. Furthermore, you must be aware of the following aspects:

  • Several functions allow for reentrancy, however we refrain from adding a mutex lock to keep it as use-case agnostic as possible. Please ensure at the protocol level that potentially malicious reentrant calls do not affect your smart contract system.

  • In the functions:

    we strongly recommend implementing a permissioned deploy protection by setting the first 20 bytes equal to msg.sender in the salt to prevent maliciously intended frontrun proxy deployments on other chains.

  • The target EVM version for compilation is set to paris, i.e. neither the contract creation bytecode of CreateX nor the returned runtime bytecode contains a PUSH0 instruction.

  • Please refer to our comment in the discussion here for background information on the risks of our private-key-based deployment approach. We recommend verifying prior to interacting with CreateX on any chain, that the keccak256 hash of the broadcasted contract creation bytecode is 0x12ec861579b63a3ab9db3b5a23c57d56402ad3061475b088f17054e2f2daf22f or of the deployed runtime bytecode is 0xbd8a7ea8cfca7b4e5f5041d7d4b17bc317c5ce42cfbc42066a00cf26b43eb53f.

Tests

For all (fuzz) tests available in the test directory, we have consistently applied the Branching Tree Technique with bulloak. This means that each test file is accompanied by a .tree file that defines all the necessary branches to be tested.

Example (CreateX._guard.tree):

CreateX_Guard_Internal_Test
├── When the first 20 bytes of the salt equals the caller
│   ├── When the 21st byte of the salt equals 0x01
│   │   └── It should return the keccak256 hash of the ABI-encoded values msg.sender, block.chainid, and the salt.
│   ├── When the 21st byte of the salt equals 0x00
│   │   └── It should return the keccak256 hash of the ABI-encoded values msg.sender and the salt.
│   └── When the 21st byte of the salt is greater than 0x01
│       └── It should revert.
├── When the first 20 bytes of the salt equals the zero address
│   ├── When the 21st byte of the salt equals 0x01
│   │   └── It should return the keccak256 hash of the ABI-encoded values block.chainid and the salt.
│   ├── When the 21st byte of the salt equals 0x00
│   │   └── It should return the keccak256 hash of the ABI-encoded value salt.
│   └── When the 21st byte of the salt is greater than 0x01
│       └── It should revert.
└── When the first 20 bytes of the salt do not equal the caller or the zero address
    ├── It should return the keccak256 hash of the ABI-encoded value salt.
    └── When the salt value is generated pseudo-randomly
        └── It should return the unmodified salt value.

Test Coverage

This project repository uses forge coverage. Simply run:

forge coverage

In order to generate an HTML file with the coverage data, you can invoke:

pnpm coverage:report

The written tests available in the directory test achieve a test coverage of 100% for the CreateX contract:

| File            | % Lines           | % Statements      | % Branches      | % Funcs         |
|-----------------|-------------------|-------------------|-----------------|-----------------|
| src/CreateX.sol | 100.00% (149/149) | 100.00% (210/210) | 100.00% (78/78) | 100.00% (31/31) |

Important

A test coverage of 100% does not mean that there are no vulnerabilities. What really counts is the quality and spectrum of the tests themselves!

ABI (Application Binary Interface)

Tip

If you forge install this repository, the Solidity-based interface can also be found here.

Solidity
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.4;

/**
 * @title CreateX Factory Interface Definition
 * @author pcaversaccio (https://web.archive.org/web/20230921103111/https://pcaversaccio.com/)
 * @custom:coauthor Matt Solomon (https://web.archive.org/web/20230921103335/https://mattsolomon.dev/)
 */
interface ICreateX {
  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                            TYPES                           */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  struct Values {
    uint256 constructorAmount;
    uint256 initCallAmount;
  }

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                           EVENTS                           */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  event ContractCreation(address indexed newContract, bytes32 indexed salt);
  event ContractCreation(address indexed newContract);
  event Create3ProxyContractCreation(
    address indexed newContract,
    bytes32 indexed salt
  );

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                        CUSTOM ERRORS                       */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  error FailedContractCreation(address emitter);
  error FailedContractInitialisation(address emitter, bytes revertData);
  error InvalidSalt(address emitter);
  error InvalidNonceValue(address emitter);
  error FailedEtherTransfer(address emitter, bytes revertData);

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                           CREATE                           */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  function deployCreate(
    bytes memory initCode
  ) external payable returns (address newContract);

  function deployCreateAndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values,
    address refundAddress
  ) external payable returns (address newContract);

  function deployCreateAndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values
  ) external payable returns (address newContract);

  function deployCreateClone(
    address implementation,
    bytes memory data
  ) external payable returns (address proxy);

  function computeCreateAddress(
    address deployer,
    uint256 nonce
  ) external view returns (address computedAddress);

  function computeCreateAddress(
    uint256 nonce
  ) external view returns (address computedAddress);

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                           CREATE2                          */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  function deployCreate2(
    bytes32 salt,
    bytes memory initCode
  ) external payable returns (address newContract);

  function deployCreate2(
    bytes memory initCode
  ) external payable returns (address newContract);

  function deployCreate2AndInit(
    bytes32 salt,
    bytes memory initCode,
    bytes memory data,
    Values memory values,
    address refundAddress
  ) external payable returns (address newContract);

  function deployCreate2AndInit(
    bytes32 salt,
    bytes memory initCode,
    bytes memory data,
    Values memory values
  ) external payable returns (address newContract);

  function deployCreate2AndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values,
    address refundAddress
  ) external payable returns (address newContract);

  function deployCreate2AndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values
  ) external payable returns (address newContract);

  function deployCreate2Clone(
    bytes32 salt,
    address implementation,
    bytes memory data
  ) external payable returns (address proxy);

  function deployCreate2Clone(
    address implementation,
    bytes memory data
  ) external payable returns (address proxy);

  function computeCreate2Address(
    bytes32 salt,
    bytes32 initCodeHash,
    address deployer
  ) external pure returns (address computedAddress);

  function computeCreate2Address(
    bytes32 salt,
    bytes32 initCodeHash
  ) external view returns (address computedAddress);

  /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
  /*                           CREATE3                          */
  /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

  function deployCreate3(
    bytes32 salt,
    bytes memory initCode
  ) external payable returns (address newContract);

  function deployCreate3(
    bytes memory initCode
  ) external payable returns (address newContract);

  function deployCreate3AndInit(
    bytes32 salt,
    bytes memory initCode,
    bytes memory data,
    Values memory values,
    address refundAddress
  ) external payable returns (address newContract);

  function deployCreate3AndInit(
    bytes32 salt,
    bytes memory initCode,
    bytes memory data,
    Values memory values
  ) external payable returns (address newContract);

  function deployCreate3AndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values,
    address refundAddress
  ) external payable returns (address newContract);

  function deployCreate3AndInit(
    bytes memory initCode,
    bytes memory data,
    Values memory values
  ) external payable returns (address newContract);

  function computeCreate3Address(
    bytes32 salt,
    address deployer
  ) external pure returns (address computedAddress);

  function computeCreate3Address(
    bytes32 salt
  ) external view returns (address computedAddress);
}
ethers.js
[
  "error FailedContractCreation(address)",
  "error FailedContractInitialisation(address,bytes)",
  "error FailedEtherTransfer(address,bytes)",
  "error InvalidNonceValue(address)",
  "error InvalidSalt(address)",
  "event ContractCreation(address indexed,bytes32 indexed)",
  "event ContractCreation(address indexed)",
  "event Create3ProxyContractCreation(address indexed,bytes32 indexed)",
  "function computeCreate2Address(bytes32,bytes32) view returns (address)",
  "function computeCreate2Address(bytes32,bytes32,address) pure returns (address)",
  "function computeCreate3Address(bytes32,address) pure returns (address)",
  "function computeCreate3Address(bytes32) view returns (address)",
  "function computeCreateAddress(uint256) view returns (address)",
  "function computeCreateAddress(address,uint256) view returns (address)",
  "function deployCreate(bytes) payable returns (address)",
  "function deployCreate2(bytes32,bytes) payable returns (address)",
  "function deployCreate2(bytes) payable returns (address)",
  "function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
  "function deployCreate2AndInit(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)) payable returns (address)",
  "function deployCreate2Clone(bytes32,address,bytes) payable returns (address)",
  "function deployCreate2Clone(address,bytes) payable returns (address)",
  "function deployCreate3(bytes) payable returns (address)",
  "function deployCreate3(bytes32,bytes) payable returns (address)",
  "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
  "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address)",
  "function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address)",
  "function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address) 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)"
]
viem
[
  "error FailedContractCreation(address emitter)",
  "error FailedContractInitialisation(address emitter, bytes revertData)",
  "error FailedEtherTransfer(address emitter, bytes revertData)",
  "error InvalidNonceValue(address emitter)",
  "error InvalidSalt(address emitter)",
  "event ContractCreation(address indexed newContract, bytes32 indexed salt)",
  "event ContractCreation(address indexed newContract)",
  "event Create3ProxyContractCreation(address indexed newContract, bytes32 indexed salt)",
  "function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) view returns (address computedAddress)",
  "function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer) pure returns (address computedAddress)",
  "function computeCreate3Address(bytes32 salt, address deployer) pure returns (address computedAddress)",
  "function computeCreate3Address(bytes32 salt) view returns (address computedAddress)",
  "function computeCreateAddress(uint256 nonce) view returns (address computedAddress)",
  "function computeCreateAddress(address deployer, uint256 nonce) view returns (address computedAddress)",
  "function deployCreate(bytes initCode) payable returns (address newContract)",
  "function deployCreate2(bytes32 salt, bytes initCode) payable returns (address newContract)",
  "function deployCreate2(bytes initCode) payable returns (address newContract)",
  "function deployCreate2AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
  "function deployCreate2AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
  "function deployCreate2AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
  "function deployCreate2AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
  "function deployCreate2Clone(bytes32 salt, address implementation, bytes data) payable returns (address proxy)",
  "function deployCreate2Clone(address implementation, bytes data) payable returns (address proxy)",
  "function deployCreate3(bytes initCode) payable returns (address newContract)",
  "function deployCreate3(bytes32 salt, bytes initCode) payable returns (address newContract)",
  "function deployCreate3AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
  "function deployCreate3AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
  "function deployCreate3AndInit(bytes32 salt, bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
  "function deployCreate3AndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
  "function deployCreateAndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values) payable returns (address newContract)",
  "function deployCreateAndInit(bytes initCode, bytes data, (uint256 constructorAmount, uint256 initCallAmount) values, address refundAddress) payable returns (address newContract)",
  "function deployCreateClone(address implementation, bytes data) payable returns (address proxy)",
] as const;
JSON
[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "emitter",
        "type": "address"
      }
    ],
    "name": "FailedContractCreation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "emitter",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "revertData",
        "type": "bytes"
      }
    ],
    "name": "FailedContractInitialisation",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "emitter",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "revertData",
        "type": "bytes"
      }
    ],
    "name": "FailedEtherTransfer",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "emitter",
        "type": "address"
      }
    ],
    "name": "InvalidNonceValue",
    "type": "error"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "emitter",
        "type": "address"
      }
    ],
    "name": "InvalidSalt",
    "type": "error"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "ContractCreation",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "name": "ContractCreation",
    "type": "event"
  },
  {
    "anonymous": false,
    "inputs": [
      {
        "indexed": true,
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      },
      {
        "indexed": true,
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "Create3ProxyContractCreation",
    "type": "event"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes32",
        "name": "initCodeHash",
        "type": "bytes32"
      }
    ],
    "name": "computeCreate2Address",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes32",
        "name": "initCodeHash",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "deployer",
        "type": "address"
      }
    ],
    "name": "computeCreate2Address",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "deployer",
        "type": "address"
      }
    ],
    "name": "computeCreate3Address",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "pure",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      }
    ],
    "name": "computeCreate3Address",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      }
    ],
    "name": "computeCreateAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "deployer",
        "type": "address"
      },
      {
        "internalType": "uint256",
        "name": "nonce",
        "type": "uint256"
      }
    ],
    "name": "computeCreateAddress",
    "outputs": [
      {
        "internalType": "address",
        "name": "computedAddress",
        "type": "address"
      }
    ],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      }
    ],
    "name": "deployCreate",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      }
    ],
    "name": "deployCreate2",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      }
    ],
    "name": "deployCreate2",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "refundAddress",
        "type": "address"
      }
    ],
    "name": "deployCreate2AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      }
    ],
    "name": "deployCreate2AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "refundAddress",
        "type": "address"
      }
    ],
    "name": "deployCreate2AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      }
    ],
    "name": "deployCreate2AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "deployCreate2Clone",
    "outputs": [
      {
        "internalType": "address",
        "name": "proxy",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "deployCreate2Clone",
    "outputs": [
      {
        "internalType": "address",
        "name": "proxy",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      }
    ],
    "name": "deployCreate3",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      }
    ],
    "name": "deployCreate3",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      }
    ],
    "name": "deployCreate3AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      }
    ],
    "name": "deployCreate3AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes32",
        "name": "salt",
        "type": "bytes32"
      },
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "refundAddress",
        "type": "address"
      }
    ],
    "name": "deployCreate3AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "refundAddress",
        "type": "address"
      }
    ],
    "name": "deployCreate3AndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      }
    ],
    "name": "deployCreateAndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "bytes",
        "name": "initCode",
        "type": "bytes"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      },
      {
        "components": [
          {
            "internalType": "uint256",
            "name": "constructorAmount",
            "type": "uint256"
          },
          {
            "internalType": "uint256",
            "name": "initCallAmount",
            "type": "uint256"
          }
        ],
        "internalType": "struct CreateX.Values",
        "name": "values",
        "type": "tuple"
      },
      {
        "internalType": "address",
        "name": "refundAddress",
        "type": "address"
      }
    ],
    "name": "deployCreateAndInit",
    "outputs": [
      {
        "internalType": "address",
        "name": "newContract",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  },
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "implementation",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "data",
        "type": "bytes"
      }
    ],
    "name": "deployCreateClone",
    "outputs": [
      {
        "internalType": "address",
        "name": "proxy",
        "type": "address"
      }
    ],
    "stateMutability": "payable",
    "type": "function"
  }
]

New Deployment(s)

We offer two options for deploying CreateX to your desired chain:

  1. Deploy it yourself by using one of the pre-signed transactions. Details can be found in the subsequent paragraph.
  2. Request a deployment by opening an issue. You can significantly reduce the time to deployment by sending funds to cover the deployment cost (a reliable amount with a small tip 😏 would be ~0.3 ETH) to the deployer account: 0xeD456e05CaAb11d66C4c797dD6c1D6f9A7F352b5.

Caution

Prior to using a pre-signed transaction, you MUST ensure that the gas metering of the target chain is EQUIVALENT to that of Ethereum's EVM version!

The default pre-signed transaction has a gas limit of 3,000,000 gas, so if the target chain requires more than 3 million gas to deploy, the contract creation transaction will revert and we will not be able to deploy CreateX to the address 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed. In this case, the only way to get CreateX deployed at the expected address is for the chain to store the contract there as a predeploy.

If you are not sure how to validate this, you can either use the eth_estimateGas JSON-RPC method or simply deploy the CreateX contract from another account and see how much gas is needed for the deployment. Standard EVM chains should require exactly 2,580,902 gas to deploy CreateX.

We repeat: PLEASE DO NOT BROADCAST ANY PRE-SIGNED TRANSACTION WITHOUT LOCAL TESTING! Also, before deploying, you MUST send at least 0.3 ETH to the deployer address 0xeD456e05CaAb11d66C4c797dD6c1D6f9A7F352b5. We offer three pre-signed, pre-EIP-155 transactions with the same gas price of 100 gwei, but different gasLimit levels:

You can broadcast the transaction using either ethers.js or cast:

It is recommended to install pnpm through the npm package manager, which comes bundled with Node.js when you install it on your system. It is recommended to use a Node.js version >= 20.0.0.

Once you have npm installed, you can run the following both to install and upgrade pnpm:

npm install -g pnpm

After having installed pnpm, simply run:

git clone https://github.com/pcaversaccio/createx.git
cd createx
pnpm install

Now configure your target chain in the hardhat.config.ts file with the networks and etherscan properties, or use one of the preconfigured network configurations. After you have locally ensured that the gasLimit of 3 million works on your target chain, you can invoke:

npx hardhat run --no-compile --network <NETWORK_NAME> scripts/deploy.ts

The deploy.ts script ensures that CreateX is automatically verified if you have configured the etherscan property accordingly. The current script broadcasts the default pre-signed transaction, which has a gas limit of 3,000,000 gas. If you want to use a different pre-signed transaction, you must change the import of the pre-signed transaction in the deploy.ts script.

It is recommended to install Foundry via:

curl -L https://foundry.paradigm.xyz | bash
foundryup

To broadcast a pre-signed transaction, you can invoke:

# $TX is the pre-signed transaction.
# $RPC_URL is the RPC URL of the target chain to which you want to deploy.
cast publish $TX --rpc-url $RPC_URL

You must verify the CreateX contract separately, as specified in the next section.

Important

After deployment, please open a pull request that updates the deployments.json file and the CreateX Deployments section with the new deployment so that other users can easily know that it has been deployed.

Contract Verification

To verify a deployed CreateX contract on a block explorer, use the following parameters:

  • Verification Method / Compiler Type: Solidity (Standard JSON Input),
  • Compiler Version: v0.8.23+commit.f704f362,
  • Open Source License Type: GNU Affero General Public License (GNU AGPLv3),
  • Standard Input JSON File: Upload the file here,
  • Constructor Arguments ABI-Encoded: Leave empty.

Important

We removed the metadata hash bytecodeHash from the bytecode in order to guarantee a deterministic compilation across all operating systems. This implies that all sourcify.eth verifications have partial verification, as opposed to perfect verification, which requires a matching metadata hash.

CreateX Deployments

📌 The deployment address of CreateX is 0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed.

Tip

The complete list with additional chain information per deployment can be retrieved via createx.rocks. In addition, all sourcify.eth verification links (for the chains that are supported by sourcify.eth) can be found in the deployments.json file.

EVM-Based Production Networks

Ethereum Test Networks

Additional EVM-Based Test Networks

Integration With External Tooling

A list of external tooling that integrates with CreateX:

Community-Maintained Dune Dashboards

🙏🏼 Acknowledgement

All the work has been done jointly by myself and Matt Solomon as a public good for our ecosystem. Public good software is not just code; it's the embodiment of collective progress, a testament to collaboration's power, and a canvas where innovation meets the needs of the many. I hope we can live up to these principles! 🫡

createx's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

createx's Issues

💥 Compute Vanity Addresses

Describe the desired feature:

Hi,

In the past, my developers employed Create2 to precompute the vanity address, with a pleasing format such as 0x9999... But it seems that we cannot do that with CreateX.
Are we missing something or CreateX hasn't supported that feature yet?

Code example that solves the feature:

No response

💥 Create Dune Dashboard to Track Usage Metrics

Describe the desired feature:

It would be nice to see stats per chain, and aggregated across all chains (that Dune supports), such as:

  1. Total deployments,
  2. Count of CREATE vs. CREATE2 vs. CREAT3 deployments,
  3. Count of regular vs. EIP-1167 deployments,
  4. Counts of each non-view method calls.

Open to other ideas for metrics too.

📋 Unit and Fuzzing Tests

Functions

public Functions

CREATE

  • function deployCreate(bytes) payable returns (address) (assigned: @pcaversaccio, PR: #14)
  • function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address) (assigned: @pcaversaccio, PR: #14)
  • function deployCreateAndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address) (assigned: @pcaversaccio, PR: #14)
  • function deployCreateClone(address,bytes) payable returns (address) (address)` (assigned: @pcaversaccio, PR: #14)
  • function computeCreateAddress(uint256) view returns (address) (address)` (assigned: @pcaversaccio, PR: #14)
  • function computeCreateAddress(address,uint256) view returns (address) (address)` (assigned: @pcaversaccio, PR: #14)

CREATE2

  • function deployCreate2(bytes32,bytes) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2(bytes) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2Clone(bytes32,address,bytes) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function deployCreate2Clone(address,bytes) payable returns (address) (assigned: @pcaversaccio, PR: #17)
  • function computeCreate2Address(bytes32,bytes32) view returns (address) (assigned: @pcaversaccio, PR: #17)
  • function computeCreate2Address(bytes32,bytes32,address) pure returns (address) (assigned: @pcaversaccio, PR: #17)

CREATE3

  • function computeCreate3Address(bytes32,address) pure returns (address) (assigned: @pcaversaccio, PR: #18)
  • function computeCreate3Address(bytes32) view returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3(bytes) payable returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3(bytes32,bytes) payable returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256)) payable returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256)) payable returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3AndInit(bytes32,bytes,bytes,tuple(uint256,uint256),address) payable returns (address) (assigned: @pcaversaccio, PR: #18)
  • function deployCreate3AndInit(bytes,bytes,tuple(uint256,uint256),address) payable returns (address) (assigned: @pcaversaccio, PR: #18)

internal Functions

  • function _guard(bytes32) view returns (bytes32) (assigned: @pcaversaccio, PR: #13)
  • function _parseSalt(bytes32) view returns (SenderBytes,RedeployProtectionFlag) (assigned: @pcaversaccio, PR: #11)
  • function _efficientHash(bytes32,bytes32) pure returns (bytes32) (assigned: @mds1, PR: #9)
  • function _generateSalt() view returns (bytes32) (assigned: @mds1, PR: #9)
  • function _requireSuccessfulContractCreation(address) view (assigned: @mds1, PR: #9)
  • function _requireSuccessfulContractCreation(bool,address) view (assigned: @mds1, PR: #9)
  • function _requireSuccessfulContractInitialisation(bool,bytes,address) view (assigned: @pcaversaccio, PR: #11)

Each function is considered tested if all unit and fuzz tests have been added.

  • I would like to propose one statefulFuzz (a.k.a. invariant) test (assigned: @pcaversaccio, PR: #17):
function statefulFuzz_NoEtherBalance() public {
    assertEq(createXAddr.balance, 0);
}

💥 Discussion: Comments on Current Version

Comments on the current version, which is commit bbbd299:

  • 1. Can't tell what the guard modifier is just from it's name, maybe something like preventCrossChainRedeploy but that's verbose. (#4)
  • 2. For the goal mentioned in the guard modifier comments, you only need to hash with chain ID, not with msg.sender. I'd suggest removing msg.sender for that reason. (#4)
  • 3. onlyMsgSender modifier: Wondering if this is better than hashing with msg.sender? Trying to think of tradeoffs. I know 0age uses this approach in his factory, but hashing might be more consistent (#4)
  • 4. Any objections to shortening the name to CreateX? Feels catchier which I think is good for helping people rally around it. Also means the interface that devs will use can be ICreateX which is shorter (#2)
  • 5. Regarding (bool refunded,) = msg.sender.call{value: balance}("") lines (#4):
    • If deployer is a one-time key (e.g. nick's method) this can result in struck funds
    • Cost to send the ETH may be greater than the amount being sent
    • Given above, I'd suggest removing this to keep the logic simpler, and we can add claim or skim methods something to claim any stray ETH/tokens
  • 6. Regarding (bool success,) = implementation.call{value: msg.value}(abi.encodeWithSignature("initializer()")) lines
    • 6.1 The data to pass here should be an input—we don't know what the sig will be and often it needs inputs, so we shouldn't hardcode initializer() as the sig (#4)
    • 6.2 We're calling it on the implementation but should be calling it on the newly deployed proxy (#4)
  • 7. deployCreate2(initCode): maybe include block.difficulty/prevrandao there also? (#4)
  • 8. Missing methods (#4):
    • 8.1 The create2 section is missing methods that have onlyMsgSender without guard. Sample use case: I want the same address on all chains, but don't want others to be able to deploy (#4)
    • 8.2 Similarly the create3 section is missing versions with only the guard no onlyMsgSender modifier. Sample use case: I want the same address on all chains and anyone should be able to deploy it (#4)
    • 8.3 We should make sure each deployment opcode (create/create2/create3) supports any combination of modifiers (none, guard, onlyMsgSender, both) (#4)
  • 9. Pretty sure all input bytes will be copied to memory anyway when solidity prepares the call, so changing calldata params to memory should be a bit cheaper/smaller bytecode (#3)
  • 10. What's your plan for formatting? I see // prettier-ignore comments but no package.json with prettier as a dep. I'd just use forge fmt here and define the config in foundry.toml to remove the need for a dep (#3)
  • 11. Similar question on linting, I see the // solhint-disable comments but no dep. Personally I think we can remove those also (#3)

💥 Build `CreateXCrunch`

Describe the desired feature:

To mine efficient (e.g. 0x00000000XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX) or opinionated (0xba5edXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXba5ed) Ethereum addresses for the deployed contract address, we should build a Rust or C++-based tooling that is powered by GPU support using OpenCL features. CPU support can also be part of it.

Related issues:

Possible specification

~$ createxcrunch -h
usage: createxcrunch [-h] [-V] [-f factory_address] [-pdp permissioned_deploy_protection] [-c caller_address] [-crp crosschain_redeploy_protection] [-z minimum_leading_zeros] [-m matching_pattern] [-o output_path] input_file [path_to_JSON_file]

positional argument:
  input_file              Contract creation code as JSON file.

generic program information:
  -h, --help              Show this help message and exit.
  -V, --version           Show program's version number and exit.

salt crunching configuration options:
  -f, --factory           Set the factory address. (Optional, defaults to `0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed`).
  -pdp, --permissioned    Set a permissioned deploy protection. (Optional, defaults to `false`).
  -c, --caller            Set the caller address in checksummed hex format. Required if `pdp = true`, otherwise input will be ignored.
  -crp, --crosschain      Set a cross-chain redeploy protection. (Optional, defaults to `false`).
  -z, --zeros             Minimum number of leading zeros. Example: `-z 4`. Cannot be used in combination with `-m`. (Optional, defaults to `0`).
  -m, --matching          Matching pattern for the contract address. Example: `-m ba5edXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXba5ed`. Cannot be used in combination with `-z`. (Optional, defaults to random pattern).

general output control:
  -o, --output            Set the output path for the results.

🐛 No Way to Avoid Salt Being Re-Hashed

Describe the issue:

When supplying a salt in e.g. deployCreate2(bytes32, bytes) the salt is re-hashed in the _guard function. This is super annoying as it:

  1. Makes CreateX incompatible with common salt minters such as create2crunch (although the cross-chain protection byte requires a minor modification)
  2. Causes a 2x slow-down in vanity address mining (makes address derivation require 2x keccak hashes rather than just 1 with normal create2)

Maybe I'm just being stupid and missing the obvious method that lets you do a CREATE2 deployment while being able to directly choose the salt but I can't see it.

Code example to reproduce the issue:

Relevant code:

    function _guard(bytes32 salt) internal view returns (bytes32 guardedSalt) {
        (SenderBytes senderBytes, RedeployProtectionFlag redeployProtectionFlag) = _parseSalt({salt: salt});

        if (senderBytes == SenderBytes.MsgSender && redeployProtectionFlag == RedeployProtectionFlag.True) {
            // Configures a permissioned deploy protection as well as a cross-chain redeploy protection.
-           guardedSalt = keccak256(abi.encode(msg.sender, block.chainid, salt));
        } else if (senderBytes == SenderBytes.MsgSender && redeployProtectionFlag == RedeployProtectionFlag.False) {
            // Configures solely a permissioned deploy protection.
-           guardedSalt = _efficientHash({a: bytes32(uint256(uint160(msg.sender))), b: salt});
        } else if (senderBytes == SenderBytes.MsgSender) {
            // Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
            revert InvalidSalt({emitter: _SELF});
        } else if (senderBytes == SenderBytes.ZeroAddress && redeployProtectionFlag == RedeployProtectionFlag.True) {
            // Configures solely a cross-chain redeploy protection. In order to prevent a pseudo-randomly
            // generated cross-chain redeploy protection, we enforce the zero address check for the first 20 bytes.
-           guardedSalt = _efficientHash({a: bytes32(block.chainid), b: salt});
        } else if (
            senderBytes == SenderBytes.ZeroAddress && redeployProtectionFlag == RedeployProtectionFlag.Unspecified
        ) {
            // Reverts if the 21st byte is greater than `0x01` in order to enforce developer explicitness.
            revert InvalidSalt({emitter: _SELF});
        } else {
            // For the non-pseudo-random cases, the salt value `salt` is hashed to prevent the safeguard mechanisms
            // from being bypassed. Otherwise, the salt value `salt` is not modified.
-           guardedSalt = (salt != _generateSalt()) ? keccak256(abi.encode(salt)) : salt;
        }
    }

Relevant log output:

No response

♻️ Use String Numbers for `assert` Statements

Fixes #14 (comment). Example:

assertEq(newContract, computedAddress, "100");
assertNotEq(newContract, zeroAddress, "200");
assertNotEq(newContract.code.length, 0, "300");
assertEq(newContract.balance, 0, "400");
assertEq(ERC20Mock(computedAddress).name(), arg1, "500");
assertEq(ERC20Mock(computedAddress).symbol(), arg2, "600");
assertEq(ERC20Mock(computedAddress).balanceOf(arg3), arg4, "700");

Clarification request - PK management of CreateX deployer

CreateX is awesome contract to ensure having the same address on the different EVM chains.

It is assumed that CreateX will be deployed on the future / not yet rolled out EVM chains.

In order to satisfy this assumption following risks shall be addressed:

  • risk of loss of deployer's PK (this would result into impossibility to deploy to the new chains having replay protection )

  • risk of leakage of deployer's PK (this could result in frontrunning the CreateX deployment transaction)

Can you please address these risks and describe mitigations here / into README?

⚡️ Gas Reports Summary for `CreateX`

Context

Gas reports for CreateX:

forge test --gas-report

I get the impression that the forge gas reports are not so reliable in profiling the gas costs for CreateX (some numbers are just blatantly wrong).

Summary Runtime Costs

image

Source.

My personal opinion is that we should not use a via_ir = true configuration due to the following reasons:

  • Deployment costs should not matter (via_ir wins here),
  • The new pipeline doesn't offer any huge gas savings (I always use the default optimiser steps dhfoDgvulfnTUtnIf),
  • Block explorer verification will be easier by not using via_ir since otherwise you must upload a standard JSON input file as Etherscan and Blockscout don't allow in the UI to configure the optimiser settings.

➛ My personal opinion is to choose optimizer_runs = 6_942_000 since optimizer_runs = 10_000_000 doesn't offer any additional significant gas savings & because of the meme factor 😄.

Option 1

Configuration:

  • optimizer = true
  • optimizer_runs = 6_942_000
  • via_ir = false
| src/CreateX.sol:CreateX contract                                    |                 |                     |                     |                     |         |
|---------------------------------------------------------------------|-----------------|---------------------|---------------------|---------------------|---------|
| Deployment Cost                                                     | Deployment Size |                     |                     |                     |         |
| 2370524                                                             | 12054           |                     |                     |                     |         |
| Function Name                                                       | min             | avg                 | median              | max                 | # calls |
| computeCreate2Address(bytes32,bytes32)                              | 559             | 559                 | 559                 | 559                 | 1       |
| computeCreate2Address(bytes32,bytes32,address)                      | 580             | 580                 | 580                 | 580                 | 37      |
| computeCreate3Address(bytes32)                                      | 551             | 551                 | 551                 | 551                 | 1       |
| computeCreate3Address(bytes32,address)                              | 648             | 648                 | 648                 | 648                 | 13      |
| computeCreateAddress(address,uint256)                               | 582             | 907                 | 918                 | 1011                | 9       |
| computeCreateAddress(uint256)                                       | 539             | 739                 | 739                 | 940                 | 2       |
| deployCreate                                                        | 32866           | 4468696730258534196 | 4468696730258690252 | 8937393460516723417 | 4       |
| deployCreate2(bytes)                                                | 34455           | 1787478692103751147 | 672889              | 8937393460516702614 | 5       |
| deployCreate2(bytes32,bytes)                                        | 34228           | 2234348365129359616 | 353432              | 8937393460516697372 | 4       |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256))                 | 35063           | 893739346052220707  | 683843              | 8937393460516689843 | 10      |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256),address)         | 35154           | 893739346052222859  | 683936              | 8937393460516689730 | 10      |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 1883            | 1117174182564938042 | 681904              | 8937393460516721268 | 8       |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 2787            | 1489565576753136221 | 685888              | 8937393460516690343 | 6       |
| deployCreate2Clone(address,bytes)                                   | 48537           | 1787478692103388783 | 74846               | 8937393460516690542 | 5       |
| deployCreate2Clone(bytes32,address,bytes)                           | 48413           | 1787478692103389766 | 77011               | 8937393460516692031 | 5       |
| deployCreate3(bytes)                                                | 77062           | 3790412405130131143 | 724440              | 8937393460516392418 | 7       |
| deployCreate3(bytes32,bytes)                                        | 1254            | 4433785037053310919 | 4398873343848424821 | 8937393460516392781 | 4       |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256))                 | 45706           | 1477928345684875189 | 735349              | 8937393460516361104 | 12      |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256),address)         | 45664           | 1477928345684879200 | 735462              | 8937393460516361934 | 12      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 2712            | 2533591449744862282 | 732319              | 8937393460516362238 | 7       |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 2787            | 2412080621446464731 | 732329              | 8937393460516362773 | 11      |
| deployCreateAndInit(bytes,bytes,(uint256,uint256))                  | 33399           | 1117174182564945483 | 679699              | 8937393460516702624 | 8       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256),address)          | 33602           | 1489565576753248846 | 684813              | 8937393460516702481 | 6       |
| deployCreateClone                                                   | 53260           | 2979131153505611705 | 75423               | 8937393460516706433 | 3       |

Option 2

Configuration:

  • optimizer = true
  • optimizer_runs = 10_000_000
  • via_ir = false
| src/CreateX.sol:CreateX contract                                    |                 |                     |                     |                     |         |
|---------------------------------------------------------------------|-----------------|---------------------|---------------------|---------------------|---------|
| Deployment Cost                                                     | Deployment Size |                     |                     |                     |         |
| 2370524                                                             | 12054           |                     |                     |                     |         |
| Function Name                                                       | min             | avg                 | median              | max                 | # calls |
| computeCreate2Address(bytes32,bytes32)                              | 559             | 559                 | 559                 | 559                 | 1       |
| computeCreate2Address(bytes32,bytes32,address)                      | 580             | 580                 | 580                 | 580                 | 43      |
| computeCreate3Address(bytes32)                                      | 551             | 551                 | 551                 | 551                 | 1       |
| computeCreate3Address(bytes32,address)                              | 648             | 648                 | 648                 | 648                 | 17      |
| computeCreateAddress(address,uint256)                               | 582             | 908                 | 964                 | 1011                | 9       |
| computeCreateAddress(uint256)                                       | 539             | 763                 | 763                 | 987                 | 2       |
| deployCreate                                                        | 32866           | 4468696730258534060 | 4468696730258690281 | 8937393460516722812 | 4       |
| deployCreate2(bytes)                                                | 34455           | 1787478692103749828 | 672889              | 8937393460516696018 | 5       |
| deployCreate2(bytes32,bytes)                                        | 2101            | 3574957384206698057 | 34248               | 8937393460516722935 | 5       |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256))                 | 35063           | 893739346052218787  | 683843              | 8937393460516690742 | 10      |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256),address)         | 35154           | 893739346052223528  | 683936              | 8937393460516689718 | 10      |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 34138           | 1276770494359928442 | 683559              | 8937393460516691677 | 7       |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 1961            | 1489565576753130647 | 685878              | 8937393460516689238 | 6       |
| deployCreate2Clone(address,bytes)                                   | 55145           | 2234348365129223652 | 74846               | 8937393460516689771 | 4       |
| deployCreate2Clone(bytes32,address,bytes)                           | 1367            | 1489565576752828240 | 75322               | 8937393460516692169 | 6       |
| deployCreate3(bytes)                                                | 77062           | 3790412405130131095 | 724440              | 8937393460516392256 | 7       |
| deployCreate3(bytes32,bytes)                                        | 76801           | 4422147805985031721 | 4398873343848423992 | 8937393460516392754 | 6       |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256))                 | 45553           | 1477928345684875038 | 735349              | 8937393460516361197 | 12      |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256),address)         | 77746           | 1612285468019864591 | 735462              | 8937393460516361028 | 11      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 2699            | 1773514014821628288 | 734785              | 8937393460516364250 | 10      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 1961            | 993043717835569402  | 734413              | 8937393460516361887 | 9       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256))                  | 33399           | 2234348365129531318 | 684741              | 8937393460516721014 | 8       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256),address)          | 33602           | 1489565576753250245 | 684813              | 8937393460516704174 | 6       |
| deployCreateClone                                                   | 46560           | 1489565576752829119 | 49949               | 8937393460516706274 | 6       |

Option 3

Configuration:

  • optimizer = true
  • optimizer_runs = 6_942_000
  • via_ir = true

☠️💀
image

| src/CreateX.sol:CreateX contract                                    |                 |                     |                     |                     |         |
|---------------------------------------------------------------------|-----------------|---------------------|---------------------|---------------------|---------|
| Deployment Cost                                                     | Deployment Size |                     |                     |                     |         |
| 1860013                                                             | 9428            |                     |                     |                     |         |
| Function Name                                                       | min             | avg                 | median              | max                 | # calls |
| computeCreate2Address(bytes32,bytes32)                              | 623             | 623                 | 623                 | 623                 | 1       |
| computeCreate2Address(bytes32,bytes32,address)                      | 770             | 770                 | 770                 | 770                 | 42      |
| computeCreate3Address(bytes32)                                      | 571             | 571                 | 571                 | 571                 | 1       |
| computeCreate3Address(bytes32,address)                              | 607             | 607                 | 607                 | 607                 | 15      |
| computeCreateAddress(address,uint256)                               | 500             | 833                 | 840                 | 956                 | 9       |
| computeCreateAddress(uint256)                                       | 350             | 520                 | 520                 | 690                 | 2       |
| deployCreate                                                        | 32838           | 2234348365129382625 | 393851              | 8937393460516709962 | 4       |
| deployCreate2(bytes)                                                | 34256           | 1787478692103800627 | 757523              | 8937393460516696311 | 5       |
| deployCreate2(bytes32,bytes)                                        | 33336           | 4468696730258552594 | 4468696730258727279 | 8937393460516722482 | 4       |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256))                 | 35056           | 744782788376911022  | 767015              | 8937393460516689916 | 12      |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256),address)         | 35178           | 812490314592995098  | 768254              | 8937393460516690131 | 11      |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 2239            | 1117174182564976152 | 767692              | 8937393460516690333 | 8       |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 35033           | 1489565576753306014 | 771309              | 8937393460516690212 | 6       |
| deployCreate2Clone(address,bytes)                                   | 54904           | 2234348365129223361 | 74491               | 8937393460516689558 | 4       |
| deployCreate2Clone(bytes32,address,bytes)                           | 53945           | 2234348365129224383 | 76584               | 8937393460516690422 | 4       |
| deployCreate3(bytes)                                                | 76910           | 3790412405130160460 | 799837              | 8937393460516371615 | 7       |
| deployCreate3(bytes32,bytes)                                        | 77005           | 4422147805985053801 | 4398873343848462424 | 8937393460516372836 | 6       |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256))                 | 77401           | 1612285468019912131 | 810018              | 8937393460516341162 | 11      |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256),address)         | 45644           | 1477928345684928134 | 810408              | 8937393460516341280 | 12      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 1780            | 2216892518526867853 | 808577              | 8937393460516342214 | 8       |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 2239            | 1612285468019698086 | 809416              | 8937393460516341585 | 11      |
| deployCreateAndInit(bytes,bytes,(uint256,uint256))                  | 33532           | 1489565576753305759 | 768900              | 8937393460516702944 | 6       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256),address)          | 33611           | 1117174182564986586 | 760989              | 8937393460516703673 | 8       |
| deployCreateClone                                                   | 53326           | 2979131153505611625 | 75391               | 8937393460516706158 | 3       |

Option 4

Configuration:

  • optimizer = true
  • optimizer_runs = 10_000_000
  • via_ir = true

☠️💀
image

| src/CreateX.sol:CreateX contract                                    |                 |                     |                     |                     |         |
|---------------------------------------------------------------------|-----------------|---------------------|---------------------|---------------------|---------|
| Deployment Cost                                                     | Deployment Size |                     |                     |                     |         |
| 1860013                                                             | 9428            |                     |                     |                     |         |
| Function Name                                                       | min             | avg                 | median              | max                 | # calls |
| computeCreate2Address(bytes32,bytes32)                              | 623             | 623                 | 623                 | 623                 | 1       |
| computeCreate2Address(bytes32,bytes32,address)                      | 770             | 770                 | 770                 | 770                 | 41      |
| computeCreate3Address(bytes32)                                      | 571             | 571                 | 571                 | 571                 | 1       |
| computeCreate3Address(bytes32,address)                              | 607             | 607                 | 607                 | 607                 | 15      |
| computeCreateAddress(address,uint256)                               | 500             | 816                 | 840                 | 955                 | 9       |
| computeCreateAddress(uint256)                                       | 350             | 520                 | 520                 | 690                 | 2       |
| deployCreate                                                        | 32838           | 2979131153505832505 | 754858              | 8937393460516709820 | 3       |
| deployCreate2(bytes)                                                | 34256           | 1787478692103800716 | 757523              | 8937393460516696759 | 5       |
| deployCreate2(bytes32,bytes)                                        | 2129            | 4468696730258363881 | 4468696730258365250 | 8937393460516722895 | 4       |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256))                 | 35087           | 812490314592992551  | 768150              | 8937393460516690644 | 11      |
| deployCreate2AndInit(bytes,bytes,(uint256,uint256),address)         | 35276           | 893739346052291025  | 768254              | 8937393460516689488 | 10      |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 3118            | 1986087435670725602 | 767440              | 8937393460516720825 | 9       |
| deployCreate2AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 3062            | 993043717835623063  | 767371              | 8937393460516691473 | 9       |
| deployCreate2Clone(address,bytes)                                   | 48211           | 1489565576752831652 | 64697               | 8937393460516689582 | 6       |
| deployCreate2Clone(bytes32,address,bytes)                           | 47882           | 1787478692103389438 | 76506               | 8937393460516691581 | 5       |
| deployCreate3(bytes)                                                | 76910           | 4416329190450909174 | 4398873343848462089 | 8937393460516370947 | 8       |
| deployCreate3(bytes32,bytes)                                        | 76863           | 4422147805985053576 | 4398873343848462122 | 8937393460516371785 | 6       |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256))                 | 70701           | 1612285468019911552 | 810018              | 8937393460516341258 | 11      |
| deployCreate3AndInit(bytes,bytes,(uint256,uint256),address)         | 77791           | 1612285468019917388 | 810408              | 8937393460516340412 | 11      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256))         | 1780            | 812490314592701599  | 45081               | 8937393460516341757 | 11      |
| deployCreate3AndInit(bytes32,bytes,bytes,(uint256,uint256),address) | 3131            | 2216892518526881854 | 812739              | 8937393460516342081 | 8       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256))                  | 33532           | 1489565576753304705 | 768900              | 8937393460516703320 | 6       |
| deployCreateAndInit(bytes,bytes,(uint256,uint256),address)          | 33599           | 1276770494359981812 | 765475              | 8937393460516704003 | 7       |
| deployCreateClone                                                   | 53326           | 2979131153505611556 | 75391               | 8937393460516705953 | 3       |

💥 Build a Simple SDK for Use Without Knowledge of the Interface, ABI, or Typechain Definitions

Describe the desired feature:

Would be great if we could have some simple function exposed that could be used to deploy contracts with ethers.js provider ( or viem provider but I don't use viem ).

Something like

async function deployCreateX(signer: SignerWithAddress, salt: BytesLike, initCode: bytesLike): Promise<ContractResponse> {
   return await CreateX.deployCreate2(salt, initCode);
}

But with this function we don't need to import createx's ABI or type definitions for every projects.

Code example that solves the feature:

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.