Skip to content

Commit

Permalink
Split IUniversalFactory and UniversalFactory
Browse files Browse the repository at this point in the history
  • Loading branch information
Lohann committed Oct 2, 2024
1 parent 0195215 commit e6e3e6a
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 209 deletions.
2 changes: 1 addition & 1 deletion src/FactoryUtils.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import {IUniversalFactory} from "./UniversalFactory.sol";
import {IUniversalFactory} from "./IUniversalFactory.sol";

/**
* @title A library with helper methods for compute `CREATE2` and `CREATE3` addresses.
Expand Down
187 changes: 187 additions & 0 deletions src/IUniversalFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/**
* @dev The type of create operation being used by the current context.
*/
enum CreateKind {
CREATE2,
CREATE3
}

/**
* @dev Contract Creation Context, this struct is used to provide useful information
* to the contract constructor, such as the sender, value, call depth, etc. Without affecting
* the final address of the contract.
*/
struct Context {
address contractAddress;
address sender;
uint8 callDepth;
CreateKind kind;
bool hasCallback;
bytes4 callbackSelector;
uint256 value;
bytes32 salt;
bytes data;
}

/**
* @dev The interface exposed by the Universal Factory Contract.
*/
interface IUniversalFactory {
/**
* @dev The create2 `creationCode` reverted.
*/
error Create2Failed();

/**
* @dev The create3 `creationCode` reverted.
* obs: Called by `Create3Proxy` using `CREATE` OPCODE.
*/
error Create3Failed();

/**
* @dev The `callback` reverted, this error wraps the revert reason returned by the callback.
*/
error CallbackFailed(bytes);

/**
* @dev The deterministic address already exists.
*/
error ContractAlreadyExists(address);

/**
* @dev The provided `initCode` is reserved for internal use only, try to use `create3` instead.
*/
error ReservedInitCode();

/**
* @dev Maximum call stack of 127 exceeded.
* OBS: probably impossible to reach this limit, due EIP-150 `all but one 64th`.
*/
error CallStackOverflow();

/**
* @dev Emitted when a contract is succesfully created, this is the only event emitted by the
* universal factory.
*/
event ContractCreated(
address indexed contractAddress,
bytes32 indexed creationCodeHash,
bytes32 indexed salt,
address indexed sender,
bytes32 argumentsHash,
bytes32 codeHash,
bytes32 callbackHash,
uint8 depth,
uint256 value
) anonymous;

/**
* @dev Creates an contract at a deterministic address, the final address is derived from the
* `salt` and `creationCode`, and is computed as follow:
* ```solidity
* return address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), keccak256(creationCode))))));
* ```
* The contract constructor can access the actual sender and other information by calling `context()`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode) external payable returns (address);

/**
* @dev Same as `create2(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);

/**
* @dev Same as `create2(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);

/**
* Creates an contract at a deterministic address, the final address is derived exclusively from the `salt` field:
* ```solidity
* salt = keccak256(abi.encodePacked(msg.sender, salt));
* bytes32 proxyHash = 0x0281a97663cf81306691f0800b13a91c4d335e1d772539f127389adae654ffc6;
* address proxy = address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), proxyHash)))));
* return address(uint160(uint256(keccak256(abi.encodePacked(uint16(0xd694), proxy, uint8(1))))));
* ```
* The contract constructor can access the actual sender and other informations by calling `context()`.
*
* @param salt Salt of the contract creation, resulting address will be derivated from this value only.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode) external payable returns (address);

/**
* @dev Same as `create3(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);

/**
* @dev Same as `create3(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);

/**
* @dev Current call context, returns zero for all fields if there's no context.
* This function provides useful information that can be accessed inside the contract constructor.
* Examples:
* - `Context.contractAddress` the address of the contract being created.
* - `Context.sender` actual `msg.sender` who called the UniversalFactory.
* - `Context.data` to send arguments to the contract constructor without change the resulting address.
* - `Context.hasCallback` whether a callback was provided or not.
* - `Context.callbackSelector` first 4 bytes of the callback calldata (zero if no callback is provided).
* - `Context.callDepth` current call depth, incremented when nested contracts are created.
* - `Context.salt` the salt used to derive this contract address.
* - `Context.kind` whether `CREATE2` or `CREATE3` is used.
* - `Context.value` the value provided, this minimal value between `msg.value` and `address(this).balance` due EVM compatibility.
* @return Context current call text, or zero for all values if there's no context.
*/
function context() external view returns (Context memory);
}
185 changes: 0 additions & 185 deletions src/UniversalFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,191 +31,6 @@
*/
pragma solidity ^0.8.0;

/**
* @dev The type of create operation being used by the current context.
*/
enum CreateKind {
CREATE2,
CREATE3
}

/**
* @dev Contract Creation Context, this struct is used to provide useful information
* to the contract constructor, such as the sender, value, call depth, etc. Without affecting
* the final address of the contract.
*/
struct Context {
address contractAddress;
address sender;
uint8 callDepth;
CreateKind kind;
bool hasCallback;
bytes4 callbackSelector;
uint256 value;
bytes32 salt;
bytes data;
}

/**
* @dev The interface exposed by the Universal Factory Contract.
*/
interface IUniversalFactory {
/**
* @dev The create2 `creationCode` reverted.
*/
error Create2Failed();

/**
* @dev The create3 `creationCode` reverted.
* obs: Called by `Create3Proxy` using `CREATE` OPCODE.
*/
error Create3Failed();

/**
* @dev The `callback` reverted, this error wraps the revert reason returned by the callback.
*/
error CallbackFailed(bytes);

/**
* @dev The deterministic address already exists.
*/
error ContractAlreadyExists(address);

/**
* @dev The provided `initCode` is reserved for internal use only, try to use `create3` instead.
*/
error ReservedInitCode();

/**
* @dev Maximum call stack of 127 exceeded.
* OBS: probably impossible to reach this limit, due EIP-150 `all but one 64th`.
*/
error CallStackOverflow();

/**
* @dev Emitted when a contract is succesfully created, this is the only event emitted by the
* universal factory.
*/
event ContractCreated(
address indexed contractAddress,
bytes32 indexed creationCodeHash,
bytes32 indexed salt,
address indexed sender,
bytes32 argumentsHash,
bytes32 codeHash,
bytes32 callbackHash,
uint8 depth,
uint256 value
) anonymous;

/**
* @dev Creates an contract at a deterministic address, the final address is derived from the
* `salt` and `creationCode`, and is computed as follow:
* ```solidity
* return address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), keccak256(creationCode))))));
* ```
* The contract constructor can access the actual sender and other information by calling `context()`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode) external payable returns (address);

/**
* @dev Same as `create2(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);

/**
* @dev Same as `create2(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create2(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);

/**
* Creates an contract at a deterministic address, the final address is derived exclusively from the `salt` field:
* ```solidity
* salt = keccak256(abi.encodePacked(msg.sender, salt));
* bytes32 proxyHash = 0x0281a97663cf81306691f0800b13a91c4d335e1d772539f127389adae654ffc6;
* address proxy = address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), address(factory), uint256(salt), proxyHash)))));
* return address(uint160(uint256(keccak256(abi.encodePacked(uint16(0xd694), proxy, uint8(1))))));
* ```
* The contract constructor can access the actual sender and other informations by calling `context()`.
*
* @param salt Salt of the contract creation, resulting address will be derivated from this value only.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode) external payable returns (address);

/**
* @dev Same as `create3(uint256,bytes)`, but also includes `arguments` which will be available at `context.data`.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments)
external
payable
returns (address);

/**
* @dev Same as `create3(uint256,bytes,bytes)`, but also includes a callback used to call the contract after it is created.
* @notice The `context.hasCallback` is always set to `true`, this method ALWAYS calls the callback, even if it is empty.
* IMPORTANT 1: Throws an `CallbackFailed` error if the callback reverts.
* IMPORTANT 2: Funds sent to this method will be forwarded to the `callback`, not the contract constructor.
*
* @param salt Salt of the contract creation, this value affect the resulting address.
* @param creationCode Creation code (constructor) of the contract to be deployed, this value doesn't affect the resulting address.
* @param arguments data that will be available at `Context.data`, this field doesn't affect the resulting address.
* @param callback callback called after create the contract, this field doesn't affect the resulting address.
* @return address of the created contract.
*/
function create3(bytes32 salt, bytes calldata creationCode, bytes calldata arguments, bytes calldata callback)
external
payable
returns (address);

/**
* @dev Current call context, returns zero for all fields if there's no context.
* This function provides useful information that can be accessed inside the contract constructor.
* Examples:
* - `Context.contractAddress` the address of the contract being created.
* - `Context.sender` actual `msg.sender` who called the UniversalFactory.
* - `Context.data` to send arguments to the contract constructor without change the resulting address.
* - `Context.hasCallback` whether a callback was provided or not.
* - `Context.callbackSelector` first 4 bytes of the callback calldata (zero if no callback is provided).
* - `Context.callDepth` current call depth, incremented when nested contracts are created.
* - `Context.salt` the salt used to derive this contract address.
* - `Context.kind` whether `CREATE2` or `CREATE3` is used.
* - `Context.value` the value provided, this minimal value between `msg.value` and `address(this).balance` due EVM compatibility.
* @return Context current call text, or zero for all values if there's no context.
*/
function context() external view returns (Context memory);
}

/**
* @title Universal Factory Contract
* @author Lohann Paterno Coutinho Ferreira
Expand Down
3 changes: 2 additions & 1 deletion test/UniversalFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ pragma solidity ^0.8.27;
import {Test, console} from "forge-std/Test.sol";
import {StdUtils} from "forge-std/StdUtils.sol";
import {Vm} from "forge-std/Vm.sol";
import {CreateKind, Context, IUniversalFactory, UniversalFactory} from "../src/UniversalFactory.sol";
import {UniversalFactory} from "../src/UniversalFactory.sol";
import {CreateKind, Context, IUniversalFactory} from "../src/IUniversalFactory.sol";
import {FactoryUtils} from "../src/FactoryUtils.sol";
import {ReservedContract} from "./helpers/ReservedContract.t.sol";
import {NestedCreate} from "./helpers/NestedCreate.t.sol";
Expand Down
Loading

0 comments on commit e6e3e6a

Please sign in to comment.