From d03d852cdf721a608fd71b19f8ff8b47edd61c7e Mon Sep 17 00:00:00 2001 From: Marco Peyfuss Date: Mon, 8 Jan 2024 16:24:13 -0700 Subject: [PATCH] TLUniversalDeployer coded & tested --- .gitmodules | 9 + foundry.toml | 2 +- lib/forge-std | 1 + lib/openzeppelin-contracts | 1 + lib/openzeppelin-contracts-upgradeable | 1 + remappings.txt | 3 +- src/TLUniversalDeployer.sol | 102 ++++---- test/TLUniversalDeployer.t.sol | 317 ++++++++++++++++++++++++- test/utils/ERC1155.sol | 18 ++ test/utils/ERC721.sol | 16 +- 10 files changed, 412 insertions(+), 58 deletions(-) create mode 160000 lib/forge-std create mode 160000 lib/openzeppelin-contracts create mode 160000 lib/openzeppelin-contracts-upgradeable create mode 100644 test/utils/ERC1155.sol diff --git a/.gitmodules b/.gitmodules index e69de29..9296efd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/foundry.toml b/foundry.toml index c146f4d..f1ed67b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ optimizer = true optimizer_runs = 20000 verbosity = 3 wrap_comments = true -gas_reports = [""] +gas_reports = ["TLUniversalDeployer"] fs_permissions = [{ access = "read", path = "./"}] [fuzz] diff --git a/lib/forge-std b/lib/forge-std new file mode 160000 index 0000000..155d547 --- /dev/null +++ b/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 155d547c449afa8715f538d69454b83944117811 diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..01ef448 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 01ef448981be9d20ca85f2faf6ebdf591ce409f3 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..fbdb824 --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit fbdb824a735891908d5588b28e0da5852d7ed7ba diff --git a/remappings.txt b/remappings.txt index 6038aca..03d587a 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,4 +2,5 @@ ds-test/=lib/forge-std/lib/ds-test/src/ erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/ forge-std/=lib/forge-std/src/ -openzeppelin/=lib/openzeppelin-contracts/contracts \ No newline at end of file +openzeppelin/=lib/openzeppelin-contracts/contracts +openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts \ No newline at end of file diff --git a/src/TLUniversalDeployer.sol b/src/TLUniversalDeployer.sol index 4823860..51ecfef 100644 --- a/src/TLUniversalDeployer.sol +++ b/src/TLUniversalDeployer.sol @@ -10,7 +10,6 @@ import {Clones} from "openzeppelin/proxy/Clones.sol"; /// @author transientlabs.xyz /// @custom:version 1.0.0 contract TLUniversalDeployer is Ownable { - /*////////////////////////////////////////////////////////////////////////// Custom Types //////////////////////////////////////////////////////////////////////////*/ @@ -25,11 +24,11 @@ contract TLUniversalDeployer is Ownable { /// @dev Struct defining a contract that is deployable /// @param created A boolean spcifying if the cloneable contract struct has been created or not - /// @param type_ The contract type (human readable - ex: ERC721TL) + /// @param cType The contract type (human readable - ex: ERC721TL) /// @param versions An array of `ContractVersion` structs that are deployable struct DeployableContract { bool created; - string type_; + string cType; ContractVersion[] versions; } @@ -49,9 +48,15 @@ contract TLUniversalDeployer is Ownable { /// @param sender The msg sender /// @param deployedContract The address of the deployed contract /// @param implementation The address of the implementation contract - /// @param type_ The type of contract deployed + /// @param cType The type of contract deployed /// @param version The version of contract deployed - event ContractDeployed(address indexed sender, address indexed deployedContract, address indexed implementation, string type_, string version); + event ContractDeployed( + address indexed sender, + address indexed deployedContract, + address indexed implementation, + string cType, + string version + ); /*////////////////////////////////////////////////////////////////////////// Custom Errors @@ -87,7 +92,6 @@ contract TLUniversalDeployer is Ownable { // verify contract is valid if (!dc.created) revert InvalidDeployableContract(); - if (dc.versions.length == 0) revert InvalidDeployableContract(); // get latest version ContractVersion memory cv = dc.versions[dc.versions.length - 1]; @@ -107,7 +111,6 @@ contract TLUniversalDeployer is Ownable { // verify cloneable contract is valid if (!dc.created) revert InvalidDeployableContract(); - if (dc.versions.length == 0) revert InvalidDeployableContract(); if (versionIndex >= dc.versions.length) revert InvalidDeployableContract(); // get latest version @@ -121,35 +124,24 @@ contract TLUniversalDeployer is Ownable { Admin Functions //////////////////////////////////////////////////////////////////////////*/ - /// @notice Function to add a contract type + /// @notice Function to add a contract type and/or version /// @dev Restricted to only owner - function addDeployableContract(string calldata type_, ContractVersion calldata version) external onlyOwner { + /// @param contractType The contract type to save this under + /// @param version The version to push to the DeployableContract struct + function addDeployableContract(string calldata contractType, ContractVersion calldata version) external onlyOwner { // get DeployableContract - bytes32 dcId = keccak256(bytes(type_)); - DeployableContract storage dc = _deployableContracts[dcId]; - - // check to see if it's been created or not - if (dc.created) revert ContractAlreadyCreated(); - - // save to storage - dc.created = true; - dc.type_ = type_; - dc.versions.push(version); - _deployableContractKeys.push(dcId); - } - - /// @notice Function to add a contract version - /// @dev Restricted to only owner - /// @dev Does not check if the version exists or not - function addContractVersion(string calldata type_, ContractVersion calldata version) external onlyOwner { - // get DeployableContract - bytes32 dcId = keccak256(bytes(type_)); + bytes32 dcId = keccak256(bytes(contractType)); DeployableContract storage dc = _deployableContracts[dcId]; - // verify contract is valid - if (!dc.created) revert InvalidDeployableContract(); + // if the contract type has not been created, create. e + // else, skip and just push version. + if (!dc.created) { + dc.created = true; + dc.cType = contractType; + _deployableContractKeys.push(dcId); + } - // add version + // push version dc.versions.push(version); } @@ -158,10 +150,10 @@ contract TLUniversalDeployer is Ownable { //////////////////////////////////////////////////////////////////////////*/ /// @notice Function to get contracts that can be deployed - function getDeployableContracts() external view returns (DeployableContract[] memory) { - DeployableContract[] memory dcs = new DeployableContract[](_deployableContractKeys.length); + function getDeployableContracts() external view returns (string[] memory) { + string[] memory dcs = new string[](_deployableContractKeys.length); for (uint256 i = 0; i < _deployableContractKeys.length; i++) { - dcs[i] = _deployableContracts[_deployableContractKeys[i]]; + dcs[i] = _deployableContracts[_deployableContractKeys[i]].cType; } return dcs; @@ -179,25 +171,26 @@ contract TLUniversalDeployer is Ownable { /// @notice Function to predict the address at which a contract would be deployed /// @dev Predicts for the latest implementation + /// @param sender The sender of the contract deployment transaction /// @param contractType The contract type to deploy /// @param initializationCode The initialization code to call after contract deployment - function predictDeployedContractAddress(string calldata contractType, bytes calldata initializationCode) external view returns (address) { + function predictDeployedContractAddress( + address sender, + string calldata contractType, + bytes calldata initializationCode + ) external view returns (address) { // get DeployableContract bytes32 dcId = keccak256(bytes(contractType)); DeployableContract memory dc = _deployableContracts[dcId]; // verify contract is valid if (!dc.created) revert InvalidDeployableContract(); - if (dc.versions.length == 0) revert InvalidDeployableContract(); // get latest version ContractVersion memory cv = dc.versions[dc.versions.length - 1]; // create salt by hashing the sender and init code - bytes32 salt = keccak256(abi.encodePacked( - msg.sender, - initializationCode - )); + bytes32 salt = keccak256(abi.encodePacked(sender, initializationCode)); // predict return Clones.predictDeterministicAddress(cv.implementation, salt); @@ -205,27 +198,29 @@ contract TLUniversalDeployer is Ownable { /// @notice Function to predict the address at which a contract would be deployed /// @dev Predicts for a specific implementation + /// @param sender The sender of the contract deployment transaction /// @param contractType The contract type to deploy /// @param initializationCode The initialization code to call after contract deployment /// @param versionIndex The indeex of the `ContractVersion` to deploy - function predictDeployedContractAddress(string calldata contractType, bytes calldata initializationCode, uint256 versionIndex) external view returns (address) { + function predictDeployedContractAddress( + address sender, + string calldata contractType, + bytes calldata initializationCode, + uint256 versionIndex + ) external view returns (address) { // get DeployableContract bytes32 dcId = keccak256(bytes(contractType)); DeployableContract memory dc = _deployableContracts[dcId]; // verify contract is valid if (!dc.created) revert InvalidDeployableContract(); - if (dc.versions.length == 0) revert InvalidDeployableContract(); if (versionIndex >= dc.versions.length) revert InvalidDeployableContract(); // get latest version ContractVersion memory cv = dc.versions[versionIndex]; // create salt by hashing the sender and init code - bytes32 salt = keccak256(abi.encodePacked( - msg.sender, - initializationCode - )); + bytes32 salt = keccak256(abi.encodePacked(sender, initializationCode)); // predict return Clones.predictDeterministicAddress(cv.implementation, salt); @@ -236,21 +231,20 @@ contract TLUniversalDeployer is Ownable { //////////////////////////////////////////////////////////////////////////*/ /// @notice Private function to deploy contracts - function _deploy(DeployableContract memory dc, ContractVersion memory cv, bytes memory initializationCode) private { + function _deploy(DeployableContract memory dc, ContractVersion memory cv, bytes memory initializationCode) + private + { // create salt by hashing the sender and init code - bytes32 salt = keccak256(abi.encodePacked( - msg.sender, - initializationCode - )); + bytes32 salt = keccak256(abi.encodePacked(msg.sender, initializationCode)); // clone address deployedContract = Clones.cloneDeterministic(cv.implementation, salt); // initialize - (bool success, ) = deployedContract.call(initializationCode); + (bool success,) = deployedContract.call(initializationCode); if (!success) revert InitializationFailed(); // emit event - emit ContractDeployed(msg.sender, deployedContract, cv.implementation, dc.type_, cv.id); + emit ContractDeployed(msg.sender, deployedContract, cv.implementation, dc.cType, cv.id); } -} \ No newline at end of file +} diff --git a/test/TLUniversalDeployer.t.sol b/test/TLUniversalDeployer.t.sol index 30c8459..69fb80c 100644 --- a/test/TLUniversalDeployer.t.sol +++ b/test/TLUniversalDeployer.t.sol @@ -2,4 +2,319 @@ pragma solidity 0.8.22; import "forge-std/Test.sol"; -import {TLUniversalDeployer} from "src/TLUniversalDeployer.sol"; +import {Strings} from "openzeppelin/utils/Strings.sol"; +import {Clones, Ownable, TLUniversalDeployer} from "src/TLUniversalDeployer.sol"; +import {ERC721} from "test/utils/ERC721.sol"; +import {ERC1155} from "test/utils/ERC1155.sol"; + +contract TLUniversalDeployerTest is Test { + using Strings for uint256; + + TLUniversalDeployer public tlud; + ERC721 public erc721; + ERC1155 public erc1155; + ERC721 public erc721v2; + ERC1155 public erc1155v2; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + event ContractDeployed( + address indexed sender, + address indexed deployedContract, + address indexed implementation, + string cType, + string version + ); + + function setUp() public { + tlud = new TLUniversalDeployer(address(this)); + erc721 = new ERC721(); + erc1155 = new ERC1155(); + erc721v2 = new ERC721(); + erc1155v2 = new ERC1155(); + } + + /// @dev Test constructor + function test_constructor(address initOwner) public { + // limit fuzz + vm.assume(initOwner != address(0)); + + // deploy + vm.expectEmit(true, true, true, true); + emit OwnershipTransferred(address(0), initOwner); + TLUniversalDeployer dep = new TLUniversalDeployer(initOwner); + + // test init values + assertEq(dep.owner(), initOwner); + string[] memory dcs = dep.getDeployableContracts(); + assertEq(dcs.length, 0); + } + + /// @dev Test access controlled functions + function test_accessControl(address hacker, string memory id, address implementation) public { + // limit fuzz + vm.assume(hacker != address(this)); + + // create contract version + TLUniversalDeployer.ContractVersion memory cv = + TLUniversalDeployer.ContractVersion({id: id, implementation: implementation}); + + // test if hacker can access admin functions + vm.startPrank(hacker); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, hacker)); + tlud.addDeployableContract("ERC721", cv); + vm.stopPrank(); + + // invariant + string[] memory dcs = tlud.getDeployableContracts(); + assertEq(dcs.length, 0); + } + + /// @dev Test adding contract types + function test_addDeployableContract(uint256 numContracts) public { + // limit fuzz + if (numContracts > 200) { + numContracts = numContracts % 200; + } + + // variables + string memory contractType; + string[] memory dcs; + TLUniversalDeployer.ContractVersion memory cv; + TLUniversalDeployer.DeployableContract memory dc; + + // loop and create + for (uint256 i = 0; i < numContracts; i++) { + contractType = string(abi.encodePacked("Contract_", i.toString())); + cv = TLUniversalDeployer.ContractVersion("1", address(uint160(i))); + tlud.addDeployableContract(contractType, cv); + dcs = tlud.getDeployableContracts(); + dc = tlud.getDeployableContract(contractType); + assertEq(dcs[dcs.length - 1], contractType); + assertTrue(dc.created); + assertEq(dc.cType, contractType); + assertEq(dc.versions.length, 1); + assertEq(dc.versions[0].id, "1"); + assertEq(dc.versions[0].implementation, address(uint160(i))); + } + + // invariant + assertEq(dcs.length, numContracts); + } + + /// @dev Test adding contract versions + function test_addDeployableContract_versions(uint256 numVersions) public { + // limit fuzz + if (numVersions > 200) { + numVersions = numVersions % 200; + } + if (numVersions == 0) { + numVersions = 1; + } + // variables + string[] memory dcs; + TLUniversalDeployer.ContractVersion memory cv; + TLUniversalDeployer.DeployableContract memory dc; + + // create first version + cv = TLUniversalDeployer.ContractVersion("0", address(0)); + tlud.addDeployableContract("DC", cv); + dc = tlud.getDeployableContract("DC"); + + // loop and add versions + for (uint256 i = 1; i < numVersions; i++) { + cv = TLUniversalDeployer.ContractVersion(i.toString(), address(uint160(i))); + tlud.addDeployableContract("DC", cv); + dcs = tlud.getDeployableContracts(); + assertEq(dcs.length, 1); + dc = tlud.getDeployableContract("DC"); + cv = dc.versions[i]; + assertEq(cv.id, i.toString()); + assertEq(cv.implementation, address(uint160(i))); + } + + // invariant + assertEq(dc.versions.length, numVersions); + } + + /// @dev Test deploying latest contracts + function test_deploy_latest( + address sender, + address initOwnerOne, + address initOwnerTwo, + string memory name, + string memory symbol, + string memory uri, + uint256 num + ) public { + // limit fuzz + vm.assume(initOwnerOne != address(0)); + vm.assume(initOwnerTwo != address(0)); + vm.assume(initOwnerTwo.code.length == 0); // 1155 can't be minted to contract + + // create init codes + bytes memory init721 = abi.encodeWithSelector(ERC721.initialize.selector, initOwnerOne, name, symbol); + bytes memory init1155 = abi.encodeWithSelector(ERC1155.initialize.selector, initOwnerTwo, uri, num); + + // try deploying invalid deployable contracts + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.deploy("ERC721", init721); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC721", init721); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.deploy("ERC1155", init1155); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC1155", init1155); + + // created deployable contracts + TLUniversalDeployer.ContractVersion memory cv; + cv = TLUniversalDeployer.ContractVersion("1", address(erc721)); + tlud.addDeployableContract("ERC721", cv); + cv = TLUniversalDeployer.ContractVersion("2", address(erc721v2)); + tlud.addDeployableContract("ERC721", cv); + cv = TLUniversalDeployer.ContractVersion("1", address(erc1155)); + tlud.addDeployableContract("ERC1155", cv); + cv = TLUniversalDeployer.ContractVersion("2", address(erc1155v2)); + tlud.addDeployableContract("ERC1155", cv); + + // predict contract addresses + address c721 = tlud.predictDeployedContractAddress(sender, "ERC721", init721); + address c1155 = tlud.predictDeployedContractAddress(sender, "ERC1155", init1155); + + // deploy contracts + vm.startPrank(sender); + vm.expectEmit(true, true, true, true); + emit ContractDeployed(sender, c721, address(erc721v2), "ERC721", "2"); + tlud.deploy("ERC721", init721); + + vm.expectEmit(true, true, true, true); + emit ContractDeployed(sender, c1155, address(erc1155v2), "ERC1155", "2"); + tlud.deploy("ERC1155", init1155); + vm.stopPrank(); + + // check deployed contract data + ERC721 d721 = ERC721(c721); + assertEq(d721.owner(), initOwnerOne); + assertEq(d721.name(), name); + assertEq(d721.symbol(), symbol); + assertEq(d721.balanceOf(initOwnerOne), 1); + assertEq(d721.ownerOf(1), initOwnerOne); + + ERC1155 d1155 = ERC1155(c1155); + assertEq(d1155.owner(), initOwnerTwo); + assertEq(d1155.uri(1), uri); + assertEq(d1155.balanceOf(initOwnerTwo, 1), num); + + // try deploying again (reverts) + vm.startPrank(sender); + vm.expectRevert(Clones.ERC1167FailedCreateClone.selector); + tlud.deploy("ERC721", init721); + + vm.expectRevert(Clones.ERC1167FailedCreateClone.selector); + tlud.deploy("ERC1155", init1155); + vm.stopPrank(); + + // try deploying with invalid init code (reverts) + vm.startPrank(sender); + vm.expectRevert(TLUniversalDeployer.InitializationFailed.selector); + tlud.deploy("ERC721", init1155); + + vm.expectRevert(TLUniversalDeployer.InitializationFailed.selector); + tlud.deploy("ERC1155", init721); + vm.stopPrank(); + } + + /// @dev Test deploying specific contract version + function test_deploy_specificVersion( + address sender, + address initOwnerOne, + address initOwnerTwo, + string memory name, + string memory symbol, + string memory uri, + uint256 num + ) public { + // limit fuzz + vm.assume(initOwnerOne != address(0)); + vm.assume(initOwnerTwo != address(0)); + vm.assume(initOwnerTwo.code.length == 0); // 1155 can't be minted to contract + + // create init codes + bytes memory init721 = abi.encodeWithSelector(ERC721.initialize.selector, initOwnerOne, name, symbol); + bytes memory init1155 = abi.encodeWithSelector(ERC1155.initialize.selector, initOwnerTwo, uri, num); + + // try deploying invalid deployable contracts + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.deploy("ERC721", init721, 0); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC721", init721, 0); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.deploy("ERC1155", init1155, 0); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC1155", init1155, 0); + + // created deployable contracts + TLUniversalDeployer.ContractVersion memory cv; + cv = TLUniversalDeployer.ContractVersion("1", address(erc721)); + tlud.addDeployableContract("ERC721", cv); + cv = TLUniversalDeployer.ContractVersion("2", address(erc721v2)); + tlud.addDeployableContract("ERC721", cv); + cv = TLUniversalDeployer.ContractVersion("1", address(erc1155)); + tlud.addDeployableContract("ERC1155", cv); + cv = TLUniversalDeployer.ContractVersion("2", address(erc1155v2)); + tlud.addDeployableContract("ERC1155", cv); + + // try deploying invalid deployable contracts - invalid index + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.deploy("ERC721", init721, 2); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC721", init721, 2); + vm.expectRevert(TLUniversalDeployer.InvalidDeployableContract.selector); + tlud.predictDeployedContractAddress(sender, "ERC1155", init1155, 2); + + // predict contract addresses + address c721 = tlud.predictDeployedContractAddress(sender, "ERC721", init721, 0); + address c1155 = tlud.predictDeployedContractAddress(sender, "ERC1155", init1155, 0); + + // deploy contracts + vm.startPrank(sender); + vm.expectEmit(true, true, true, true); + emit ContractDeployed(sender, c721, address(erc721), "ERC721", "1"); + tlud.deploy("ERC721", init721, 0); + + vm.expectEmit(true, true, true, true); + emit ContractDeployed(sender, c1155, address(erc1155), "ERC1155", "1"); + tlud.deploy("ERC1155", init1155, 0); + vm.stopPrank(); + + // check deployed contract data + ERC721 d721 = ERC721(c721); + assertEq(d721.owner(), initOwnerOne); + assertEq(d721.name(), name); + assertEq(d721.symbol(), symbol); + assertEq(d721.balanceOf(initOwnerOne), 1); + assertEq(d721.ownerOf(1), initOwnerOne); + + ERC1155 d1155 = ERC1155(c1155); + assertEq(d1155.owner(), initOwnerTwo); + assertEq(d1155.uri(1), uri); + assertEq(d1155.balanceOf(initOwnerTwo, 1), num); + + // try deploying again (reverts) + vm.startPrank(sender); + vm.expectRevert(Clones.ERC1167FailedCreateClone.selector); + tlud.deploy("ERC721", init721, 0); + + vm.expectRevert(Clones.ERC1167FailedCreateClone.selector); + tlud.deploy("ERC1155", init1155, 0); + vm.stopPrank(); + + // try deploying with invalid init code (reverts) + vm.startPrank(sender); + vm.expectRevert(TLUniversalDeployer.InitializationFailed.selector); + tlud.deploy("ERC721", init1155, 0); + + vm.expectRevert(TLUniversalDeployer.InitializationFailed.selector); + tlud.deploy("ERC1155", init721, 0); + vm.stopPrank(); + } +} diff --git a/test/utils/ERC1155.sol b/test/utils/ERC1155.sol new file mode 100644 index 0000000..8da1de9 --- /dev/null +++ b/test/utils/ERC1155.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.22; + +import {ERC1155Upgradeable} from "openzeppelin-upgradeable/token/ERC1155/ERC1155Upgradeable.sol"; +import {OwnableUpgradeable} from "openzeppelin-upgradeable/access/OwnableUpgradeable.sol"; + +contract ERC1155 is ERC1155Upgradeable, OwnableUpgradeable { + constructor() { + _disableInitializers(); + } + + function initialize(address initOwner, string memory uri, uint256 num) external initializer { + __ERC1155_init(uri); + __Ownable_init(initOwner); + + _mint(initOwner, 1, num, ""); + } +} diff --git a/test/utils/ERC721.sol b/test/utils/ERC721.sol index e98ae0d..d79c2af 100644 --- a/test/utils/ERC721.sol +++ b/test/utils/ERC721.sol @@ -1,4 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.22; -import {ERC721Upgradeable} from "" \ No newline at end of file +import {ERC721Upgradeable} from "openzeppelin-upgradeable/token/ERC721/ERC721Upgradeable.sol"; +import {OwnableUpgradeable} from "openzeppelin-upgradeable/access/OwnableUpgradeable.sol"; + +contract ERC721 is ERC721Upgradeable, OwnableUpgradeable { + constructor() { + _disableInitializers(); + } + + function initialize(address initOwner, string memory name, string memory symbol) external initializer { + __ERC721_init(name, symbol); + __Ownable_init(initOwner); + + _mint(initOwner, 1); + } +}