Skip to content
Merged
7 changes: 1 addition & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ jobs:
run: |
forge --version

- name: Run Forge fmt
run: |
forge fmt --check
id: fmt

- name: Run Forge build
run: |
forge build --via-ir --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
forge test -vvvv
id: test
168 changes: 138 additions & 30 deletions test/factory/MagicDropCloneFactoryTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,27 @@ import {MagicDropTokenImplRegistry} from "../../contracts/registry/MagicDropToke
import {TokenStandard} from "../../contracts/common/Structs.sol";

contract MockERC721Initializable is MockERC721 {
function initialize(string memory, string memory, address, uint256) public {}
function initialize(
string memory,
string memory,
address,
uint256
) public {}
}

contract MockERC1155Initializable is MockERC1155 {
function initialize(string memory, string memory, address, uint256) public {}
function initialize(
string memory,
string memory,
address,
uint256
) public {}
}

contract InvalidImplementation is MockERC721 {
function initialize(string memory) public {} // Missing name and symbol parameters
function initialize(string memory) public {
revert("deliberate failure");
} // Missing name and symbol parameters
}

contract MagicDropCloneFactoryTest is Test {
Expand All @@ -34,17 +46,24 @@ contract MagicDropCloneFactoryTest is Test {

uint32 internal erc721ImplId;
uint32 internal erc1155ImplId;
InvalidImplementation invalidImplementation;

function setUp() public {
vm.startPrank(owner);

invalidImplementation = new InvalidImplementation();

// Deploy and initialize registry
address registryImpl = LibClone.clone(address(new MagicDropTokenImplRegistry()));
address registryImpl = LibClone.clone(
address(new MagicDropTokenImplRegistry())
);
registry = MagicDropTokenImplRegistry(payable(registryImpl));
registry.initialize(owner);

// Deploy factory
address factoryImpl = LibClone.clone(address(new MagicDropCloneFactory()));
address factoryImpl = LibClone.clone(
address(new MagicDropCloneFactory())
);
factory = MagicDropCloneFactory(payable(factoryImpl));
factory.initialize(owner, address(registry));

Expand All @@ -53,10 +72,19 @@ contract MagicDropCloneFactoryTest is Test {
erc1155Impl = new MockERC1155Initializable();

// Register implementations
erc721ImplId =
registry.registerImplementation(TokenStandard.ERC721, address(erc721Impl), true, 0.01 ether, 0.00001 ether);
erc721ImplId = registry.registerImplementation(
TokenStandard.ERC721,
address(erc721Impl),
true,
0.01 ether,
0.00001 ether
);
erc1155ImplId = registry.registerImplementation(
TokenStandard.ERC1155, address(erc1155Impl), true, 0.01 ether, 0.00001 ether
TokenStandard.ERC1155,
address(erc1155Impl),
true,
0.01 ether,
0.00001 ether
);

// Fund user
Expand All @@ -69,7 +97,11 @@ contract MagicDropCloneFactoryTest is Test {
vm.startPrank(user);

address newContract = factory.createContract{value: 0.01 ether}(
"TestNFT", "TNFT", TokenStandard.ERC721, payable(user), erc721ImplId
"TestNFT",
"TNFT",
TokenStandard.ERC721,
payable(user),
erc721ImplId
);

MockERC721Initializable nft = MockERC721Initializable(newContract);
Expand All @@ -84,8 +116,13 @@ contract MagicDropCloneFactoryTest is Test {
function testCreateERC721ContractWithDefaultImplementation() public {
vm.startPrank(user);

address newContract =
factory.createContract{value: 0.01 ether}("TestNFT", "TNFT", TokenStandard.ERC721, payable(user), 0);
address newContract = factory.createContract{value: 0.01 ether}(
"TestNFT",
"TNFT",
TokenStandard.ERC721,
payable(user),
0
);

MockERC721Initializable nft = MockERC721Initializable(newContract);
// Test minting
Expand All @@ -99,7 +136,11 @@ contract MagicDropCloneFactoryTest is Test {
vm.startPrank(user);

address newContract = factory.createContract{value: 0.01 ether}(
"TestMultiToken", "TMT", TokenStandard.ERC1155, payable(user), erc1155ImplId
"TestMultiToken",
"TMT",
TokenStandard.ERC1155,
payable(user),
erc1155ImplId
);

MockERC1155Initializable nft = MockERC1155Initializable(newContract);
Expand All @@ -114,8 +155,13 @@ contract MagicDropCloneFactoryTest is Test {
function testCreateERC1155ContractWithDefaultImplementation() public {
vm.startPrank(user);

address newContract =
factory.createContract{value: 0.01 ether}("TestMultiToken", "TMT", TokenStandard.ERC1155, payable(user), 0);
address newContract = factory.createContract{value: 0.01 ether}(
"TestMultiToken",
"TMT",
TokenStandard.ERC1155,
payable(user),
0
);

MockERC1155Initializable nft = MockERC1155Initializable(newContract);

Expand All @@ -133,10 +179,23 @@ contract MagicDropCloneFactoryTest is Test {
bytes32[] memory salts = new bytes32[](numSalts);

for (uint256 i = 0; i < numSalts; i++) {
salts[i] = keccak256(abi.encodePacked(i, block.timestamp, msg.sender));
address predictedAddress = factory.predictDeploymentAddress(TokenStandard.ERC721, erc721ImplId, salts[i]);
address deployedAddress = factory.createContractDeterministic{value: 0.01 ether}(
"TestNFT", "TNFT", TokenStandard.ERC721, payable(user), erc721ImplId, salts[i]
salts[i] = keccak256(
abi.encodePacked(i, block.timestamp, msg.sender)
);
address predictedAddress = factory.predictDeploymentAddress(
TokenStandard.ERC721,
erc721ImplId,
salts[i]
);
address deployedAddress = factory.createContractDeterministic{
value: 0.01 ether
}(
"TestNFT",
"TNFT",
TokenStandard.ERC721,
payable(user),
erc721ImplId,
salts[i]
);
assertEq(predictedAddress, deployedAddress);
}
Expand All @@ -149,60 +208,103 @@ contract MagicDropCloneFactoryTest is Test {

vm.prank(user);
vm.expectRevert();
factory.createContract("TestNFT", "TNFT", TokenStandard.ERC721, payable(user), invalidImplId);
factory.createContract(
"TestNFT",
"TNFT",
TokenStandard.ERC721,
payable(user),
invalidImplId
);
}

function testCreateDeterministicContractWithSameSalt() public {
vm.startPrank(user);

factory.createContractDeterministic{value: 0.01 ether}(
"TestNFT1", "TNFT1", TokenStandard.ERC721, payable(user), erc721ImplId, bytes32(0)
"TestNFT1",
"TNFT1",
TokenStandard.ERC721,
payable(user),
erc721ImplId,
bytes32(uint256(100))
);
vm.expectRevert();
factory.createContractDeterministic{value: 0.01 ether}(
"TestNFT2", "TNFT2", TokenStandard.ERC721, payable(user), erc721ImplId, bytes32(0)
"TestNFT2",
"TNFT2",
TokenStandard.ERC721,
payable(user),
erc721ImplId,
bytes32(uint256(100))
);
}

function testContractAlreadyDeployed() public {
bytes32 salt = bytes32(uint256(1));
bytes32 salt = bytes32(uint256(101));
uint32 implId = 1;
TokenStandard standard = TokenStandard.ERC721;
address initialOwner = address(0x1);
string memory name = "TestToken";
string memory symbol = "TT";

// Predict the address where the contract will be deployed
address predictedAddress = factory.predictDeploymentAddress(standard, implId, salt);
address predictedAddress = factory.predictDeploymentAddress(
standard,
implId,
salt
);

// Deploy a dummy contract to the predicted address
vm.etch(predictedAddress, address(erc721Impl).code);
vm.expectRevert();
// Try to create a contract with the same parameters
factory.createContractDeterministic{value: 0.01 ether}(
name, symbol, standard, payable(initialOwner), implId, salt
name,
symbol,
standard,
payable(initialOwner),
implId,
salt
);
}

function testInitializationFailed() public {
TokenStandard standard = TokenStandard.ERC721;

vm.startPrank(owner);
InvalidImplementation impl = new InvalidImplementation();
uint32 implId = registry.registerImplementation(standard, address(impl), false, 0.01 ether, 0.00001 ether);

uint32 implId = registry.registerImplementation(
standard,
address(invalidImplementation),
false,
0.01 ether,
0.00001 ether
);
vm.stopPrank();

vm.expectRevert(MagicDropCloneFactory.InitializationFailed.selector);
factory.createContractDeterministic{value: 0.01 ether}(
"TestNFT", "TNFT", standard, payable(user), implId, bytes32(0)
"TestNFT",
"TNFT",
standard,
payable(user),
implId,
bytes32(uint256(102))
);
}

function testInsufficientDeploymentFee() public {
vm.startPrank(user);
vm.expectRevert(MagicDropCloneFactory.InsufficientDeploymentFee.selector);
vm.expectRevert(
MagicDropCloneFactory.InsufficientDeploymentFee.selector
);
factory.createContractDeterministic{value: 0.005 ether}(
"TestNFT", "TNFT", TokenStandard.ERC721, payable(user), erc721ImplId, bytes32(0)
"TestNFT",
"TNFT",
TokenStandard.ERC721,
payable(user),
erc721ImplId,
bytes32(uint256(103))
);
}

Expand All @@ -212,7 +314,13 @@ contract MagicDropCloneFactoryTest is Test {

function testWithdraw() public {
vm.startPrank(user);
factory.createContract{value: 0.01 ether}("TestMultiToken", "TMT", TokenStandard.ERC1155, payable(user), 0);
factory.createContract{value: 0.01 ether}(
"TestMultiToken",
"TMT",
TokenStandard.ERC1155,
payable(user),
0
);
vm.stopPrank();

vm.startPrank(owner);
Expand Down