From e6e3e6acc3f3b8a9bf0d1495c0f8fdb47d88a801 Mon Sep 17 00:00:00 2001 From: Lohann Paterno Coutinho Ferreira Date: Wed, 2 Oct 2024 10:29:21 -0300 Subject: [PATCH] Split IUniversalFactory and UniversalFactory --- src/FactoryUtils.sol | 2 +- src/IUniversalFactory.sol | 187 ++++++++++++++++++++++++++++ src/UniversalFactory.sol | 185 --------------------------- test/UniversalFactory.t.sol | 3 +- test/examples/Deployer.sol | 2 +- test/examples/Deployer.t.sol | 3 +- test/examples/Owned.sol | 2 +- test/examples/Owned.t.sol | 3 +- test/helpers/Inspector.t.sol | 2 +- test/helpers/NestedCreate.t.sol | 16 +-- test/helpers/ReservedContract.t.sol | 2 +- test/helpers/TestUtils.sol | 2 +- 12 files changed, 200 insertions(+), 209 deletions(-) create mode 100644 src/IUniversalFactory.sol diff --git a/src/FactoryUtils.sol b/src/FactoryUtils.sol index c445b90..333549c 100644 --- a/src/FactoryUtils.sol +++ b/src/FactoryUtils.sol @@ -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. diff --git a/src/IUniversalFactory.sol b/src/IUniversalFactory.sol new file mode 100644 index 0000000..ae12b53 --- /dev/null +++ b/src/IUniversalFactory.sol @@ -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); +} diff --git a/src/UniversalFactory.sol b/src/UniversalFactory.sol index 1d2c9ad..b0ba51b 100644 --- a/src/UniversalFactory.sol +++ b/src/UniversalFactory.sol @@ -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 diff --git a/test/UniversalFactory.t.sol b/test/UniversalFactory.t.sol index cb88359..090044d 100644 --- a/test/UniversalFactory.t.sol +++ b/test/UniversalFactory.t.sol @@ -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"; diff --git a/test/examples/Deployer.sol b/test/examples/Deployer.sol index a8210b1..569910f 100644 --- a/test/examples/Deployer.sol +++ b/test/examples/Deployer.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Context, IUniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "@universal-factory/IUniversalFactory.sol"; // The `Deployer` contract is responsible for deploying the `MyContract` contract. // The `arguments` contains owner's signature authorizing the deployment of a specific creationCodeHash by anyone. diff --git a/test/examples/Deployer.t.sol b/test/examples/Deployer.t.sol index 267d654..bd80b64 100644 --- a/test/examples/Deployer.t.sol +++ b/test/examples/Deployer.t.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.0; import {Test, console} from "forge-std/Test.sol"; import {StdUtils} from "forge-std/StdUtils.sol"; import {Vm, VmSafe} from "forge-std/Vm.sol"; -import {Context, IUniversalFactory, UniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {UniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "@universal-factory/IUniversalFactory.sol"; import {FactoryUtils} from "@universal-factory/FactoryUtils.sol"; import {Deployer} from "./Deployer.sol"; import {MyContract} from "./MyContract.sol"; diff --git a/test/examples/Owned.sol b/test/examples/Owned.sol index e4583c8..bb1c0f1 100644 --- a/test/examples/Owned.sol +++ b/test/examples/Owned.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Context, IUniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "@universal-factory/IUniversalFactory.sol"; contract Owned { IUniversalFactory internal constant FACTORY = IUniversalFactory(0x0000000000001C4Bf962dF86e38F0c10c7972C6E); diff --git a/test/examples/Owned.t.sol b/test/examples/Owned.t.sol index 88ed6b7..efe6970 100644 --- a/test/examples/Owned.t.sol +++ b/test/examples/Owned.t.sol @@ -4,7 +4,8 @@ pragma solidity ^0.8.0; import {Test, console} from "forge-std/Test.sol"; import {StdUtils} from "forge-std/StdUtils.sol"; import {Vm, VmSafe} from "forge-std/Vm.sol"; -import {Context, IUniversalFactory, UniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {UniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "@universal-factory/IUniversalFactory.sol"; import {FactoryUtils} from "@universal-factory/FactoryUtils.sol"; import {Owned} from "./Owned.sol"; import {TestUtils} from "../helpers/TestUtils.sol"; diff --git a/test/helpers/Inspector.t.sol b/test/helpers/Inspector.t.sol index 0fa0a8b..b51b22c 100644 --- a/test/helpers/Inspector.t.sol +++ b/test/helpers/Inspector.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {Context, IUniversalFactory} from "../../src/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "../../src/IUniversalFactory.sol"; contract Inspector { IUniversalFactory private FACTORY; diff --git a/test/helpers/NestedCreate.t.sol b/test/helpers/NestedCreate.t.sol index 5a13a77..c8fd1c6 100644 --- a/test/helpers/NestedCreate.t.sol +++ b/test/helpers/NestedCreate.t.sol @@ -1,13 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -// import {VmSafe} from "forge-std/Vm.sol"; -// import {console} from "forge-std/console.sol"; -import {Context, IUniversalFactory} from "../../src/UniversalFactory.sol"; +import {Context, IUniversalFactory} from "../../src/IUniversalFactory.sol"; contract NestedCreate { - // VmSafe internal constant VM = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); - IUniversalFactory private FACTORY; Context private _ctx; uint256 private _constructorCallValue; @@ -18,16 +14,6 @@ contract NestedCreate { FACTORY = factory; Context memory ctx = factory.context(); require(ctx.contractAddress == address(this), "Can only be created by `UniversalFactory`"); - // console.log(" address(this):", address(this)); - // console.log(" ctx.contractAddress:", ctx.contractAddress); - // console.log(" ctx.sender:", ctx.sender); - // console.log(" ctx.callDepth:", ctx.callDepth); - // console.log(" ctx.kind:", uint256(ctx.kind)); - // console.log(" ctx.hasCallback:", ctx.hasCallback); - // console.log("ctx.callbackSelector:", VM.toString(bytes32(ctx.callbackSelector))); - // console.log(" ctx.salt:", VM.toString(bytes32(ctx.salt))); - // console.log(" ctx.data:", ctx.data.length); - // console.log(""); if (ctx.hasCallback) { require(ctx.callbackSelector == NestedCreate.validateContext.selector, "invalid callback selector"); diff --git a/test/helpers/ReservedContract.t.sol b/test/helpers/ReservedContract.t.sol index 480aa77..949195b 100644 --- a/test/helpers/ReservedContract.t.sol +++ b/test/helpers/ReservedContract.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {IUniversalFactory, Context} from "../../src/UniversalFactory.sol"; +import {IUniversalFactory, Context} from "../../src/IUniversalFactory.sol"; /** * @dev Reserved contract, can only be deployed by `owner`. diff --git a/test/helpers/TestUtils.sol b/test/helpers/TestUtils.sol index fa6a6d5..eb8dcf6 100644 --- a/test/helpers/TestUtils.sol +++ b/test/helpers/TestUtils.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.27; import {Vm} from "forge-std/Vm.sol"; -import {IUniversalFactory} from "@universal-factory/UniversalFactory.sol"; +import {IUniversalFactory} from "@universal-factory/IUniversalFactory.sol"; library TestUtils { // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.