From 87c8d3f4832c0abfdc58e86eb410e7c2fd8649e0 Mon Sep 17 00:00:00 2001 From: Isabella Smallcombe Date: Wed, 20 Sep 2023 18:17:25 -0400 Subject: [PATCH] feat: add tokenId to redeemInstructionsHashIsAllowed and update unit tests --- .../ZoraCreatorRedeemMinterStrategy.sol | 26 +- .../ZoraCreatorRedeemMinterFactory.t.sol | 100 +- .../ZoraCreatorRedeemMinterStrategy.t.sol | 2965 +++++++++-------- 3 files changed, 1551 insertions(+), 1540 deletions(-) diff --git a/src/minters/redeem/ZoraCreatorRedeemMinterStrategy.sol b/src/minters/redeem/ZoraCreatorRedeemMinterStrategy.sol index 1e1201be0..74e4337e6 100644 --- a/src/minters/redeem/ZoraCreatorRedeemMinterStrategy.sol +++ b/src/minters/redeem/ZoraCreatorRedeemMinterStrategy.sol @@ -120,8 +120,8 @@ contract ZoraCreatorRedeemMinterStrategy is Enjoy, SaleStrategy, Initializable { error MintTokenContractMustBeCreatorContract(); error SenderIsNotTokenOwner(); - /// @notice keccak256(abi.encode(RedeemInstructions)) => redeem instructions are allowed - mapping(bytes32 => bool) public redeemInstructionsHashIsAllowed; + /// @notice tokenId, keccak256(abi.encode(RedeemInstructions)) => redeem instructions are allowed + mapping(uint256 => mapping(bytes32 => bool)) public redeemInstructionsHashIsAllowed; /// @notice Zora creator contract address public creatorContract; @@ -210,27 +210,33 @@ contract ZoraCreatorRedeemMinterStrategy is Enjoy, SaleStrategy, Initializable { } /// @notice Set redeem instructions + /// @param tokenId The token id to set redeem instructions for /// @param _redeemInstructions The redeem instructions object - function setRedeem(RedeemInstructions calldata _redeemInstructions) external onlyCreatorContract { + function setRedeem(uint256 tokenId, RedeemInstructions calldata _redeemInstructions) external onlyCreatorContract { + if (_redeemInstructions.mintToken.tokenId != tokenId) { + revert InvalidTokenIdsForTokenType(); + } + validateRedeemInstructions(_redeemInstructions); bytes32 hash = redeemInstructionsHash(_redeemInstructions); - if (redeemInstructionsHashIsAllowed[hash]) { + if (redeemInstructionsHashIsAllowed[tokenId][hash]) { revert RedeemInstructionAlreadySet(); } - redeemInstructionsHashIsAllowed[hash] = true; + redeemInstructionsHashIsAllowed[tokenId][hash] = true; emit RedeemSet(creatorContract, hash, _redeemInstructions); } /// @notice Clear redeem instructions + /// @param tokenId The token id to clear redeem instructions for /// @param hashes Array of redeem instructions hashes to clear - function clearRedeem(bytes32[] calldata hashes) external onlyCreatorContract { + function clearRedeem(uint256 tokenId, bytes32[] calldata hashes) external onlyCreatorContract { uint256 numHashes = hashes.length; unchecked { for (uint256 i; i < numHashes; ++i) { - redeemInstructionsHashIsAllowed[hashes[i]] = false; + redeemInstructionsHashIsAllowed[tokenId][hashes[i]] = false; } } @@ -254,7 +260,11 @@ contract ZoraCreatorRedeemMinterStrategy is Enjoy, SaleStrategy, Initializable { (RedeemInstructions, uint256[][], uint256[][]) ); bytes32 hash = redeemInstructionsHash(redeemInstructions); - if (!redeemInstructionsHashIsAllowed[hash]) { + + if (tokenId != redeemInstructions.mintToken.tokenId) { + revert InvalidTokenIdsForTokenType(); + } + if (!redeemInstructionsHashIsAllowed[tokenId][hash]) { revert RedeemInstructionNotAllowed(); } if (redeemInstructions.saleStart > block.timestamp) { diff --git a/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol b/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol index 8cf01260d..ee7a6bb3a 100644 --- a/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol +++ b/test/minters/redeem/ZoraCreatorRedeemMinterFactory.t.sol @@ -17,64 +17,64 @@ import {ZoraCreatorRedeemMinterStrategy} from "../../../src/minters/redeem/ZoraC import {ZoraCreatorRedeemMinterFactory} from "../../../src/minters/redeem/ZoraCreatorRedeemMinterFactory.sol"; /// @notice Contract versions after v1.4.0 will not support burn to redeem -// contract ZoraCreatorRedeemMinterFactoryTest is Test { -// ProtocolRewards internal protocolRewards; -// ZoraCreator1155Impl internal target; -// ZoraCreatorRedeemMinterFactory internal minterFactory; -// address payable internal tokenAdmin = payable(address(0x999)); -// address payable internal factoryAdmin = payable(address(0x888)); -// address internal zora; +contract ZoraCreatorRedeemMinterFactoryTest is Test { + ProtocolRewards internal protocolRewards; + ZoraCreator1155Impl internal target; + ZoraCreatorRedeemMinterFactory internal minterFactory; + address payable internal tokenAdmin = payable(address(0x999)); + address payable internal factoryAdmin = payable(address(0x888)); + address internal zora; -// event RedeemMinterDeployed(address indexed creatorContract, address indexed minterContract); + event RedeemMinterDeployed(address indexed creatorContract, address indexed minterContract); -// function setUp() public { -// zora = makeAddr("zora"); -// bytes[] memory emptyData = new bytes[](0); -// protocolRewards = new ProtocolRewards(); -// ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); -// Zora1155 proxy = new Zora1155(address(targetImpl)); -// target = ZoraCreator1155Impl(address(proxy)); -// target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), tokenAdmin, emptyData); + function setUp() public { + zora = makeAddr("zora"); + bytes[] memory emptyData = new bytes[](0); + protocolRewards = new ProtocolRewards(); + ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); + Zora1155 proxy = new Zora1155(address(targetImpl)); + target = ZoraCreator1155Impl(address(proxy)); + target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), tokenAdmin, emptyData); -// minterFactory = new ZoraCreatorRedeemMinterFactory(); -// } + minterFactory = new ZoraCreatorRedeemMinterFactory(); + } -// function test_contractVersion() public { -// assertEq(minterFactory.contractVersion(), "1.0.1"); -// } + function test_contractVersion() public { + assertEq(minterFactory.contractVersion(), "1.0.1"); + } -// function test_createMinterIfNoneExists() public { -// vm.startPrank(tokenAdmin); -// target.addPermission(0, address(minterFactory), target.PERMISSION_BIT_MINTER()); -// address predictedAddress = minterFactory.predictMinterAddress(address(target)); -// vm.expectEmit(false, false, false, false); -// emit RedeemMinterDeployed(address(target), predictedAddress); -// target.callSale(0, minterFactory, abi.encodeWithSelector(ZoraCreatorRedeemMinterFactory.createMinterIfNoneExists.selector)); -// vm.stopPrank(); + function test_createMinterIfNoneExists() public { + vm.startPrank(tokenAdmin); + target.addPermission(0, address(minterFactory), target.PERMISSION_BIT_MINTER()); + address predictedAddress = minterFactory.predictMinterAddress(address(target)); + vm.expectEmit(false, false, false, false); + emit RedeemMinterDeployed(address(target), predictedAddress); + target.callSale(0, minterFactory, abi.encodeWithSelector(ZoraCreatorRedeemMinterFactory.createMinterIfNoneExists.selector, 0)); + vm.stopPrank(); -// ZoraCreatorRedeemMinterStrategy minter = ZoraCreatorRedeemMinterStrategy(predictedAddress); -// assertTrue(address(minter).code.length > 0); -// } + ZoraCreatorRedeemMinterStrategy minter = ZoraCreatorRedeemMinterStrategy(predictedAddress); + assertTrue(address(minter).code.length > 0); + } -// function test_createMinterRequiresIZoraCreator1155Caller() public { -// ERC1155PresetMinterPauser randomToken = new ERC1155PresetMinterPauser("https://uri.com"); + function test_createMinterRequiresIZoraCreator1155Caller() public { + ERC1155PresetMinterPauser randomToken = new ERC1155PresetMinterPauser("https://uri.com"); -// vm.expectRevert(abi.encodeWithSignature("CallerNotZoraCreator1155()")); -// vm.prank(address(randomToken)); -// minterFactory.createMinterIfNoneExists(); -// } + vm.expectRevert(abi.encodeWithSignature("CallerNotZoraCreator1155()")); + vm.prank(address(randomToken)); + minterFactory.createMinterIfNoneExists(); + } -// function test_getDeployedMinterForCreatorContract() public { -// vm.prank(address(target)); -// minterFactory.createMinterIfNoneExists(); -// address minterAddress = minterFactory.predictMinterAddress(address(target)); + function test_getDeployedMinterForCreatorContract() public { + vm.prank(address(target)); + minterFactory.createMinterIfNoneExists(); + address minterAddress = minterFactory.predictMinterAddress(address(target)); -// assertEq(minterAddress, minterFactory.getDeployedRedeemMinterForCreatorContract(address(target))); -// } + assertEq(minterAddress, minterFactory.getDeployedRedeemMinterForCreatorContract(address(target))); + } -// function test_supportsInterface() public { -// assertTrue(minterFactory.supportsInterface(0x01ffc9a7)); // ERC165 -// assertTrue(minterFactory.supportsInterface(type(IMinter1155).interfaceId)); -// assertTrue(!minterFactory.supportsInterface(0x6467a6fc)); // old IMinter1155 -// } -// } + function test_supportsInterface() public { + assertTrue(minterFactory.supportsInterface(0x01ffc9a7)); // ERC165 + assertTrue(minterFactory.supportsInterface(type(IMinter1155).interfaceId)); + assertTrue(!minterFactory.supportsInterface(0x6467a6fc)); // old IMinter1155 + } +} diff --git a/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol b/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol index 7b7182fed..1b4f1d422 100644 --- a/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol +++ b/test/minters/redeem/ZoraCreatorRedeemMinterStrategy.t.sol @@ -15,1485 +15,1486 @@ import {IZoraCreator1155Factory} from "../../../src/interfaces/IZoraCreator1155F import {ZoraCreatorRedeemMinterStrategy} from "../../../src/minters/redeem/ZoraCreatorRedeemMinterStrategy.sol"; /// @notice Contract versions after v1.4.0 will not support burn to redeem -// contract ZoraCreatorRedeemMinterStrategyTest is Test { -// ProtocolRewards internal protocolRewards; -// ZoraCreator1155Impl internal target; -// ZoraCreatorRedeemMinterStrategy internal redeemMinter; -// address payable internal admin = payable(address(0x999)); -// uint256 internal newTokenId; -// address internal zora; - -// event RedeemSet(address indexed target, bytes32 indexed redeemsInstructionsHash, ZoraCreatorRedeemMinterStrategy.RedeemInstructions data); -// event RedeemProcessed(address indexed target, bytes32 indexed redeemsInstructionsHash, address sender, uint256[][] tokenIds, uint256[][] amounts); -// event RedeemsCleared(address indexed target, bytes32[] indexed redeemInstructionsHashes); - -// function setUp() external { -// zora = makeAddr("zora"); -// bytes[] memory emptyData = new bytes[](0); -// protocolRewards = new ProtocolRewards(); -// ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); -// Zora1155 proxy = new Zora1155(address(targetImpl)); -// target = ZoraCreator1155Impl(address(proxy)); -// target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), admin, emptyData); -// redeemMinter = new ZoraCreatorRedeemMinterStrategy(); -// redeemMinter.initialize(address(target)); -// vm.startPrank(admin); -// newTokenId = target.setupNewToken("https://zora.co/testing/token.json", 10); -// target.addPermission(newTokenId, address(redeemMinter), target.PERMISSION_BIT_MINTER()); -// vm.stopPrank(); -// } - -// function test_ContractURI() external { -// assertEq(redeemMinter.contractURI(), "https://github.com/ourzora/zora-1155-contracts/"); -// } - -// function test_ContractName() external { -// assertEq(redeemMinter.contractName(), "Redeem Minter Sale Strategy"); -// } - -// function test_Version() external { -// assertEq(redeemMinter.contractVersion(), "1.0.1"); -// } - -// function test_OnlyDropContractCanCallWriteFunctions() external { -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions; -// vm.startPrank(address(admin)); - -// vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); -// redeemMinter.setRedeem(redeemInstructions); - -// bytes32[] memory hashes = new bytes32[](0); -// vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); -// redeemMinter.clearRedeem(hashes); - -// vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); -// redeemMinter.requestMint(address(0), 0, 0, 0, bytes("")); - -// vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); -// redeemMinter.resetSale(0); - -// vm.stopPrank(); -// } - -// ///////// SET REDEEM ///////// - -// function test_SetRedeem() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); -// vm.stopPrank(); - -// vm.expectEmit(true, true, false, true); -// emit RedeemSet(address(target), keccak256(abi.encode(redeemInstructions)), redeemInstructions); -// vm.startPrank(address(target)); -// redeemMinter.setRedeem(redeemInstructions); -// vm.expectRevert(abi.encodeWithSignature("RedeemInstructionAlreadySet()")); -// redeemMinter.setRedeem(redeemInstructions); -// vm.stopPrank(); - -// assertTrue(redeemMinter.redeemInstructionsHashIsAllowed(keccak256(abi.encode(redeemInstructions)))); -// } - -// function test_SetRedeemInstructionValidation() external { -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.NULL, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// vm.startPrank(address(target)); - -// // InvalidTokenIdsForTokenType: ERC20 w/ nonzero tokenId start or end -// redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC20; -// redeemInstructions.instructions[0].tokenIdStart = 1; -// redeemInstructions.instructions[0].tokenIdEnd = 0; -// vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions[0].tokenIdStart = 0; -// redeemInstructions.instructions[0].tokenIdEnd = 1; -// vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); -// redeemMinter.setRedeem(redeemInstructions); - -// // InvalidTokenIdsForTokenType: non ERC20 w/ tokenID start > end -// redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC721; -// redeemInstructions.instructions[0].tokenIdStart = 4; -// redeemInstructions.instructions[0].tokenIdEnd = 2; -// vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.mintToken.tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155; -// vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions[0].tokenIdStart = 0; -// redeemInstructions.instructions[0].tokenIdEnd = 0; - -// // InvalidTokenType: tokenType is NULL -// redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.NULL; -// vm.expectRevert(abi.encodeWithSignature("InvalidTokenType()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155; - -// // MustBurnOrTransfer: both transferRecipient and burnFunction are 0 -// redeemInstructions.instructions[0].transferRecipient = address(0); -// redeemInstructions.instructions[0].burnFunction = bytes4(0); -// vm.expectRevert(abi.encodeWithSignature("MustBurnOrTransfer()")); -// redeemMinter.setRedeem(redeemInstructions); - -// // MustBurnOrTransfer: both transferRecipient and burnFunction are non-zero -// redeemInstructions.instructions[0].transferRecipient = address(1); -// redeemInstructions.instructions[0].burnFunction = bytes4(keccak256(bytes("burnFrom(address,uint256)"))); -// vm.expectRevert(abi.encodeWithSignature("MustBurnOrTransfer()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions[0].transferRecipient = address(0); - -// // IncorrectMintAmount -// redeemInstructions.instructions[0].amount = 0; -// vm.expectRevert(abi.encodeWithSignature("IncorrectMintAmount()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions[0].amount = 500; - -// // InvalidSaleEndOrStart: start > end -// redeemInstructions.saleStart = 1; -// redeemInstructions.saleEnd = 0; -// vm.expectRevert(abi.encodeWithSignature("InvalidSaleEndOrStart()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.saleStart = 0; -// redeemInstructions.saleEnd = type(uint64).max; - -// // InvalidSaleEndOrStart: block.timestamp > end -// redeemInstructions.saleStart = 0; -// redeemInstructions.saleEnd = 1 days; -// vm.warp(2 days); -// vm.expectRevert(abi.encodeWithSignature("InvalidSaleEndOrStart()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.saleEnd = type(uint64).max; - -// // EmptyRedeemInstructions(); -// redeemInstructions.instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](0); -// vm.expectRevert(abi.encodeWithSignature("EmptyRedeemInstructions()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.instructions = instructions; - -// // MintTokenContractMustBeCreatorContract -// redeemInstructions.mintToken.tokenContract = address(0); -// vm.expectRevert(abi.encodeWithSignature("MintTokenContractMustBeCreatorContract()")); -// redeemMinter.setRedeem(redeemInstructions); -// redeemInstructions.mintToken.tokenContract = address(target); - -// // MintTokenTypeMustBeERC1155: -// redeemInstructions.mintToken.tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC721; -// vm.expectRevert(abi.encodeWithSignature("MintTokenTypeMustBeERC1155()")); -// redeemMinter.setRedeem(redeemInstructions); -// } - -// ///////// REQUEST MINT ///////// - -// function test_MintFlowERC20() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 500); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectEmit(true, true, false, false); -// emit RedeemProcessed(address(target), keccak256(abi.encode(redeemInstructions)), tokenRecipient, tokenIds, amounts); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// assertEq(burnToken.balanceOf(tokenRecipient), 500); -// vm.stopPrank(); -// } - -// function test_MintFlowERC1155() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC1155PresetMinterPauser burnToken = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnToken.mint(address(tokenRecipient), 1, 1000, ""); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.setApprovalForAll(address(redeemMinter), true); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](1); -// tokenIds[0][0] = 1; -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// assertEq(burnToken.balanceOf(tokenRecipient, 1), 500); -// vm.stopPrank(); -// } - -// function test_MintFlowERC721() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC721PresetMinterPauserAutoId burnToken = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnToken.mint(address(tokenRecipient)); -// burnToken.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.setApprovalForAll(address(redeemMinter), true); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](2); -// tokenIds[0][0] = 0; -// tokenIds[0][1] = 1; -// uint256[][] memory amounts = new uint256[][](1); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// vm.expectRevert(); -// burnToken.ownerOf(0); -// vm.expectRevert(); -// burnToken.ownerOf(1); -// vm.stopPrank(); -// } - -// function test_MintFlowMultiple() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(tokenRecipient), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(tokenRecipient), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](3); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnTokenERC20), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) -// }); -// instructions[2] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC721), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC20.approve(address(redeemMinter), 500); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](3); -// uint256[][] memory amounts = new uint256[][](3); - -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// tokenIds[1] = new uint256[](1); -// tokenIds[1][0] = 1; -// amounts[1] = new uint256[](1); -// amounts[1][0] = 500; - -// tokenIds[2] = new uint256[](2); -// tokenIds[2][0] = 0; -// tokenIds[2][1] = 1; - -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// assertEq(burnTokenERC20.balanceOf(tokenRecipient), 500); -// assertEq(burnTokenERC1155.balanceOf(tokenRecipient, 1), 500); -// assertEq(burnTokenERC721.balanceOf(address(tokenRecipient)), 0); -// vm.stopPrank(); -// } - -// function test_MintFlowMultipleWithTransferInsteadOfBurn() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); -// address redeemTokenRecipient = address(323); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(tokenRecipient), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(tokenRecipient), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](3); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnTokenERC20), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// instructions[2] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC721), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC20.approve(address(redeemMinter), 500); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](3); -// uint256[][] memory amounts = new uint256[][](3); - -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// tokenIds[1] = new uint256[](1); -// tokenIds[1][0] = 1; -// amounts[1] = new uint256[](1); -// amounts[1][0] = 500; - -// tokenIds[2] = new uint256[](2); -// tokenIds[2][0] = 0; -// tokenIds[2][1] = 1; - -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// assertEq(burnTokenERC20.balanceOf(tokenRecipient), 500); -// assertEq(burnTokenERC20.balanceOf(redeemTokenRecipient), 500); -// assertEq(burnTokenERC1155.balanceOf(tokenRecipient, 1), 500); -// assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 1), 500); -// assertEq(burnTokenERC721.balanceOf(address(tokenRecipient)), 0); -// assertEq(burnTokenERC721.balanceOf(redeemTokenRecipient), 2); - -// vm.stopPrank(); -// } - -// function test_MintFlowTokenIdRangesForERC1155AndERC721() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); -// address redeemTokenRecipient = address(323); - -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(tokenRecipient), 7, 100, ""); -// burnTokenERC1155.mint(address(tokenRecipient), 8, 100, ""); -// burnTokenERC1155.mint(address(tokenRecipient), 9, 100, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](2); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 300, -// tokenIdStart: 7, -// tokenIdEnd: 9, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 3, -// tokenIdStart: 1, -// tokenIdEnd: 3, -// tokenContract: address(burnTokenERC721), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](2); -// uint256[][] memory amounts = new uint256[][](2); - -// tokenIds[0] = new uint256[](3); -// tokenIds[0][0] = 7; -// tokenIds[0][1] = 8; -// tokenIds[0][2] = 9; -// amounts[0] = new uint256[](3); -// amounts[0][0] = 100; -// amounts[0][1] = 100; -// amounts[0][2] = 100; - -// tokenIds[1] = new uint256[](3); -// tokenIds[1][0] = 1; -// tokenIds[1][1] = 2; -// tokenIds[1][2] = 3; - -// // detour: tokenId out of range -// tokenIds[0][0] = 6; -// vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[0][0] = 10; -// vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[0][0] = 7; -// tokenIds[1][0] = 0; -// vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[1][0] = 4; -// vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[1][0] = 1; - -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(address(target).balance, 1 ether); -// assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); -// assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 7), 100); -// assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 8), 100); -// assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 9), 100); -// assertEq(burnTokenERC721.balanceOf(redeemTokenRecipient), 3); - -// vm.stopPrank(); -// } - -// function test_MintFlowRedeemStart() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: uint64(block.timestamp + 1 days), -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 500); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectRevert(abi.encodeWithSignature("SaleHasNotStarted()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 1, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowRedeemEnd() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: uint64(1 days), -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.warp(2 days); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 1000); -// uint256[] memory tokenIds = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectRevert(abi.encodeWithSignature("SaleEnded()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 1, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowEthValue() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 500); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectRevert(abi.encodeWithSignature("WrongValueSent()")); -// target.mint{value: 0.9 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.expectRevert(abi.encodeWithSignature("WrongValueSent()")); -// target.mint{value: 1.1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowFundsRecipient() external { -// vm.startPrank(admin); - -// address fundsRecipient = address(239); -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: fundsRecipient -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 500); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// assertEq(fundsRecipient.balance, 1 ether); -// vm.stopPrank(); -// } - -// function test_MintFlowAmount() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnToken.mint(address(tokenRecipient), 1000); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.approve(address(redeemMinter), 500); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](0); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectRevert(abi.encodeWithSignature("IncorrectMintAmount()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 11, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowIncorrectNumberOfTokenIds() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC1155PresetMinterPauser burnToken = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnToken.mint(address(tokenRecipient), 1, 1000, ""); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// // instructions length != tokenIds length -// vm.startPrank(tokenRecipient); -// burnToken.setApprovalForAll(address(redeemMinter), true); -// uint256[][] memory tokenIds = new uint256[][](2); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// vm.expectRevert(abi.encodeWithSignature("IncorrectNumberOfTokenIds()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](1); -// tokenIds[0][0] = 1; - -// // ERC1155: amounts length != tokenIds length -// amounts = new uint256[][](2); -// vm.expectRevert(abi.encodeWithSignature("IncorrectNumberOfTokenIds()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// } - -// function test_MintFlowIncorrectBurnOrTransferAmount() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); -// address redeemTokenRecipient = address(323); - -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(tokenRecipient), 1, 500, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(tokenRecipient)); -// burnTokenERC721.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](2); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 300, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 3, -// tokenIdStart: 0, -// tokenIdEnd: 2, -// tokenContract: address(burnTokenERC721), -// transferRecipient: redeemTokenRecipient, -// burnFunction: 0 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](2); -// uint256[][] memory amounts = new uint256[][](2); - -// // this would be correct -// tokenIds[0] = new uint256[](1); -// tokenIds[0][0] = 1; -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; -// tokenIds[1] = new uint256[](2); -// tokenIds[1][0] = 0; -// tokenIds[1][1] = 1; - -// // ERC721: tokenids length != instruction amount -// tokenIds[1] = new uint256[](1); -// vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[1] = new uint256[](3); -// vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// tokenIds[1] = new uint256[](2); -// tokenIds[1][0] = 0; -// tokenIds[1][1] = 1; - -// // ERC1155: sum of amounts != instruction amount -// amounts[0][0] = 499; -// vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// amounts[0][0] = 501; -// vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); - -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerBurn20() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnTokenERC20), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC20.approve(address(redeemMinter), 500); - -// uint256[][] memory tokenIds = new uint256[][](1); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// vm.expectRevert(abi.encodeWithSignature("BurnFailed()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerBurn1155() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); - -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](1); -// tokenIds[0][0] = 1; -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// vm.expectRevert(abi.encodeWithSignature("BurnFailed()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerBurn721() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); - -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC721), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.prank(actualTokenOwner); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); -// vm.startPrank(tokenRecipient); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](2); -// tokenIds[0][0] = 0; -// tokenIds[0][1] = 1; -// uint256[][] memory amounts; - -// vm.expectRevert(abi.encodeWithSignature("SenderIsNotTokenOwner()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerTransfer20() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); - -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, -// amount: 500, -// tokenIdStart: 0, -// tokenIdEnd: 0, -// tokenContract: address(burnTokenERC20), -// transferRecipient: address(1), -// burnFunction: bytes4(0) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC20.approve(address(redeemMinter), 500); - -// uint256[][] memory tokenIds = new uint256[][](1); -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// vm.expectRevert("ERC20: transfer amount exceeds balance"); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerTransfer1155() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); - -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, -// amount: 500, -// tokenIdStart: 1, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC1155), -// transferRecipient: address(1), -// burnFunction: bytes4(0) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](1); -// tokenIds[0][0] = 1; -// uint256[][] memory amounts = new uint256[][](1); -// amounts[0] = new uint256[](1); -// amounts[0][0] = 500; - -// vm.expectRevert("ERC1155: insufficient balance for transfer"); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// function test_MintFlowSenderNotTokenOwnerTransfer721() external { -// vm.startPrank(admin); - -// address actualTokenOwner = address(92834); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); -// burnTokenERC20.mint(address(actualTokenOwner), 1000); -// ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); -// burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); -// ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnTokenERC721.mint(address(actualTokenOwner)); -// burnTokenERC721.mint(address(actualTokenOwner)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); - -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnTokenERC721), -// transferRecipient: address(1), -// burnFunction: bytes4(0) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnTokenERC721.setApprovalForAll(address(redeemMinter), true); - -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](2); -// tokenIds[0][0] = 0; -// tokenIds[0][1] = 1; -// uint256[][] memory amounts; - -// vm.expectRevert("ERC721: caller is not token owner or approved"); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// vm.stopPrank(); -// } - -// ///////// RESET AND CLEAR ///////// - -// function test_ResetSaleAlwaysReverts() external { -// vm.prank(address(target)); -// vm.expectRevert(abi.encodeWithSignature("MustCallClearRedeem()")); -// redeemMinter.resetSale(uint256(1)); -// } - -// function test_ClearRedeem() external { -// vm.startPrank(admin); - -// address tokenRecipient = address(322); -// vm.deal(tokenRecipient, 20 ether); - -// ERC721PresetMinterPauserAutoId burnToken = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); -// burnToken.mint(address(tokenRecipient)); -// burnToken.mint(address(tokenRecipient)); - -// ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ -// tokenContract: address(target), -// tokenId: newTokenId, -// amount: 10, -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); -// instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ -// tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, -// amount: 2, -// tokenIdStart: 0, -// tokenIdEnd: 1, -// tokenContract: address(burnToken), -// transferRecipient: address(0), -// burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) -// }); -// ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ -// mintToken: mintToken, -// instructions: instructions, -// saleStart: 0, -// saleEnd: type(uint64).max, -// ethAmount: 1 ether, -// ethRecipient: address(0) -// }); - -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, redeemInstructions)); -// bytes32[] memory hashes = new bytes32[](1); -// hashes[0] = keccak256(abi.encode(redeemInstructions)); -// vm.expectEmit(true, false, false, true); -// emit RedeemsCleared(address(target), hashes); -// target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.clearRedeem.selector, hashes)); -// vm.stopPrank(); - -// vm.startPrank(tokenRecipient); -// burnToken.setApprovalForAll(address(redeemMinter), true); -// uint256[][] memory tokenIds = new uint256[][](1); -// tokenIds[0] = new uint256[](2); -// tokenIds[0][0] = 0; -// tokenIds[0][1] = 1; -// uint256[][] memory amounts = new uint256[][](1); -// vm.expectRevert(abi.encodeWithSignature("RedeemInstructionNotAllowed()")); -// target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); -// } -// } +contract ZoraCreatorRedeemMinterStrategyTest is Test { + ProtocolRewards internal protocolRewards; + ZoraCreator1155Impl internal target; + ZoraCreatorRedeemMinterStrategy internal redeemMinter; + address payable internal admin = payable(address(0x999)); + uint256 internal newTokenId; + address internal zora; + + event RedeemSet(address indexed target, bytes32 indexed redeemsInstructionsHash, ZoraCreatorRedeemMinterStrategy.RedeemInstructions data); + event RedeemProcessed(address indexed target, bytes32 indexed redeemsInstructionsHash, address sender, uint256[][] tokenIds, uint256[][] amounts); + event RedeemsCleared(address indexed target, bytes32[] indexed redeemInstructionsHashes); + + function setUp() external { + zora = makeAddr("zora"); + bytes[] memory emptyData = new bytes[](0); + protocolRewards = new ProtocolRewards(); + ZoraCreator1155Impl targetImpl = new ZoraCreator1155Impl(0, zora, address(0), address(protocolRewards)); + Zora1155 proxy = new Zora1155(address(targetImpl)); + target = ZoraCreator1155Impl(address(proxy)); + target.initialize("test", "test", ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0)), admin, emptyData); + redeemMinter = new ZoraCreatorRedeemMinterStrategy(); + redeemMinter.initialize(address(target)); + vm.startPrank(admin); + newTokenId = target.setupNewToken("https://zora.co/testing/token.json", 10); + target.addPermission(newTokenId, address(redeemMinter), target.PERMISSION_BIT_MINTER()); + vm.stopPrank(); + } + + function test_ContractURI() external { + assertEq(redeemMinter.contractURI(), "https://github.com/ourzora/zora-1155-contracts/"); + } + + function test_ContractName() external { + assertEq(redeemMinter.contractName(), "Redeem Minter Sale Strategy"); + } + + function test_Version() external { + assertEq(redeemMinter.contractVersion(), "1.0.1"); + } + + function test_OnlyDropContractCanCallWriteFunctions() external { + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions; + vm.startPrank(address(admin)); + + vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); + redeemMinter.setRedeem(0, redeemInstructions); + + bytes32[] memory hashes = new bytes32[](0); + vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); + redeemMinter.clearRedeem(0, hashes); + + vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); + redeemMinter.requestMint(address(0), 0, 0, 0, bytes("")); + + vm.expectRevert(abi.encodeWithSignature("CallerNotCreatorContract()")); + redeemMinter.resetSale(0); + + vm.stopPrank(); + } + + ///////// SET REDEEM ///////// + + function test_SetRedeem() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + console2.log("NEW TOKEN ID", newTokenId); + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + vm.stopPrank(); + + vm.expectEmit(true, true, false, true); + emit RedeemSet(address(target), keccak256(abi.encode(redeemInstructions)), redeemInstructions); + vm.startPrank(address(target)); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + vm.expectRevert(abi.encodeWithSignature("RedeemInstructionAlreadySet()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + vm.stopPrank(); + + assertTrue(redeemMinter.redeemInstructionsHashIsAllowed(newTokenId, keccak256(abi.encode(redeemInstructions)))); + } + + function test_SetRedeemInstructionValidation() external { + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.NULL, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + vm.startPrank(address(target)); + + // InvalidTokenIdsForTokenType: ERC20 w/ nonzero tokenId start or end + redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC20; + redeemInstructions.instructions[0].tokenIdStart = 1; + redeemInstructions.instructions[0].tokenIdEnd = 0; + vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions[0].tokenIdStart = 0; + redeemInstructions.instructions[0].tokenIdEnd = 1; + vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + + // InvalidTokenIdsForTokenType: non ERC20 w/ tokenID start > end + redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC721; + redeemInstructions.instructions[0].tokenIdStart = 4; + redeemInstructions.instructions[0].tokenIdEnd = 2; + vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.mintToken.tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155; + vm.expectRevert(abi.encodeWithSignature("InvalidTokenIdsForTokenType()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions[0].tokenIdStart = 0; + redeemInstructions.instructions[0].tokenIdEnd = 0; + + // InvalidTokenType: tokenType is NULL + redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.NULL; + vm.expectRevert(abi.encodeWithSignature("InvalidTokenType()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions[0].tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155; + + // MustBurnOrTransfer: both transferRecipient and burnFunction are 0 + redeemInstructions.instructions[0].transferRecipient = address(0); + redeemInstructions.instructions[0].burnFunction = bytes4(0); + vm.expectRevert(abi.encodeWithSignature("MustBurnOrTransfer()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + + // MustBurnOrTransfer: both transferRecipient and burnFunction are non-zero + redeemInstructions.instructions[0].transferRecipient = address(1); + redeemInstructions.instructions[0].burnFunction = bytes4(keccak256(bytes("burnFrom(address,uint256)"))); + vm.expectRevert(abi.encodeWithSignature("MustBurnOrTransfer()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions[0].transferRecipient = address(0); + + // IncorrectMintAmount + redeemInstructions.instructions[0].amount = 0; + vm.expectRevert(abi.encodeWithSignature("IncorrectMintAmount()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions[0].amount = 500; + + // InvalidSaleEndOrStart: start > end + redeemInstructions.saleStart = 1; + redeemInstructions.saleEnd = 0; + vm.expectRevert(abi.encodeWithSignature("InvalidSaleEndOrStart()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.saleStart = 0; + redeemInstructions.saleEnd = type(uint64).max; + + // InvalidSaleEndOrStart: block.timestamp > end + redeemInstructions.saleStart = 0; + redeemInstructions.saleEnd = 1 days; + vm.warp(2 days); + vm.expectRevert(abi.encodeWithSignature("InvalidSaleEndOrStart()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.saleEnd = type(uint64).max; + + // EmptyRedeemInstructions(); + redeemInstructions.instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](0); + vm.expectRevert(abi.encodeWithSignature("EmptyRedeemInstructions()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.instructions = instructions; + + // MintTokenContractMustBeCreatorContract + redeemInstructions.mintToken.tokenContract = address(0); + vm.expectRevert(abi.encodeWithSignature("MintTokenContractMustBeCreatorContract()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + redeemInstructions.mintToken.tokenContract = address(target); + + // MintTokenTypeMustBeERC1155: + redeemInstructions.mintToken.tokenType = ZoraCreatorRedeemMinterStrategy.TokenType.ERC721; + vm.expectRevert(abi.encodeWithSignature("MintTokenTypeMustBeERC1155()")); + redeemMinter.setRedeem(newTokenId, redeemInstructions); + } + + ///////// REQUEST MINT ///////// + + function test_MintFlowERC20() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 500); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectEmit(true, true, false, false); + emit RedeemProcessed(address(target), keccak256(abi.encode(redeemInstructions)), tokenRecipient, tokenIds, amounts); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + assertEq(burnToken.balanceOf(tokenRecipient), 500); + vm.stopPrank(); + } + + function test_MintFlowERC1155() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC1155PresetMinterPauser burnToken = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnToken.mint(address(tokenRecipient), 1, 1000, ""); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.setApprovalForAll(address(redeemMinter), true); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](1); + tokenIds[0][0] = 1; + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + assertEq(burnToken.balanceOf(tokenRecipient, 1), 500); + vm.stopPrank(); + } + + function test_MintFlowERC721() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC721PresetMinterPauserAutoId burnToken = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnToken.mint(address(tokenRecipient)); + burnToken.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.setApprovalForAll(address(redeemMinter), true); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](2); + tokenIds[0][0] = 0; + tokenIds[0][1] = 1; + uint256[][] memory amounts = new uint256[][](1); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + vm.expectRevert(); + burnToken.ownerOf(0); + vm.expectRevert(); + burnToken.ownerOf(1); + vm.stopPrank(); + } + + function test_MintFlowMultiple() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(tokenRecipient), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(tokenRecipient), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](3); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnTokenERC20), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC1155), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) + }); + instructions[2] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC721), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC20.approve(address(redeemMinter), 500); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](3); + uint256[][] memory amounts = new uint256[][](3); + + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + tokenIds[1] = new uint256[](1); + tokenIds[1][0] = 1; + amounts[1] = new uint256[](1); + amounts[1][0] = 500; + + tokenIds[2] = new uint256[](2); + tokenIds[2][0] = 0; + tokenIds[2][1] = 1; + + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + assertEq(burnTokenERC20.balanceOf(tokenRecipient), 500); + assertEq(burnTokenERC1155.balanceOf(tokenRecipient, 1), 500); + assertEq(burnTokenERC721.balanceOf(address(tokenRecipient)), 0); + vm.stopPrank(); + } + + function test_MintFlowMultipleWithTransferInsteadOfBurn() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + address redeemTokenRecipient = address(323); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(tokenRecipient), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(tokenRecipient), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](3); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnTokenERC20), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC1155), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + instructions[2] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC721), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC20.approve(address(redeemMinter), 500); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](3); + uint256[][] memory amounts = new uint256[][](3); + + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + tokenIds[1] = new uint256[](1); + tokenIds[1][0] = 1; + amounts[1] = new uint256[](1); + amounts[1][0] = 500; + + tokenIds[2] = new uint256[](2); + tokenIds[2][0] = 0; + tokenIds[2][1] = 1; + + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + assertEq(burnTokenERC20.balanceOf(tokenRecipient), 500); + assertEq(burnTokenERC20.balanceOf(redeemTokenRecipient), 500); + assertEq(burnTokenERC1155.balanceOf(tokenRecipient, 1), 500); + assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 1), 500); + assertEq(burnTokenERC721.balanceOf(address(tokenRecipient)), 0); + assertEq(burnTokenERC721.balanceOf(redeemTokenRecipient), 2); + + vm.stopPrank(); + } + + function test_MintFlowTokenIdRangesForERC1155AndERC721() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + address redeemTokenRecipient = address(323); + + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(tokenRecipient), 7, 100, ""); + burnTokenERC1155.mint(address(tokenRecipient), 8, 100, ""); + burnTokenERC1155.mint(address(tokenRecipient), 9, 100, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](2); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 300, + tokenIdStart: 7, + tokenIdEnd: 9, + tokenContract: address(burnTokenERC1155), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 3, + tokenIdStart: 1, + tokenIdEnd: 3, + tokenContract: address(burnTokenERC721), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](2); + uint256[][] memory amounts = new uint256[][](2); + + tokenIds[0] = new uint256[](3); + tokenIds[0][0] = 7; + tokenIds[0][1] = 8; + tokenIds[0][2] = 9; + amounts[0] = new uint256[](3); + amounts[0][0] = 100; + amounts[0][1] = 100; + amounts[0][2] = 100; + + tokenIds[1] = new uint256[](3); + tokenIds[1][0] = 1; + tokenIds[1][1] = 2; + tokenIds[1][2] = 3; + + // detour: tokenId out of range + tokenIds[0][0] = 6; + vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[0][0] = 10; + vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[0][0] = 7; + tokenIds[1][0] = 0; + vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[1][0] = 4; + vm.expectRevert(abi.encodeWithSignature("TokenIdOutOfRange()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[1][0] = 1; + + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(address(target).balance, 1 ether); + assertEq(target.balanceOf(tokenRecipient, newTokenId), 10); + assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 7), 100); + assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 8), 100); + assertEq(burnTokenERC1155.balanceOf(redeemTokenRecipient, 9), 100); + assertEq(burnTokenERC721.balanceOf(redeemTokenRecipient), 3); + + vm.stopPrank(); + } + + function test_MintFlowRedeemStart() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: uint64(block.timestamp + 1 days), + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 500); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectRevert(abi.encodeWithSignature("SaleHasNotStarted()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 1, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowRedeemEnd() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: uint64(1 days), + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.warp(2 days); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 1000); + uint256[] memory tokenIds = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectRevert(abi.encodeWithSignature("SaleEnded()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 1, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowEthValue() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 500); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectRevert(abi.encodeWithSignature("WrongValueSent()")); + target.mint{value: 0.9 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.expectRevert(abi.encodeWithSignature("WrongValueSent()")); + target.mint{value: 1.1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowFundsRecipient() external { + vm.startPrank(admin); + + address fundsRecipient = address(239); + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: fundsRecipient + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 500); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + assertEq(fundsRecipient.balance, 1 ether); + vm.stopPrank(); + } + + function test_MintFlowAmount() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnToken = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnToken.mint(address(tokenRecipient), 1000); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.approve(address(redeemMinter), 500); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](0); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectRevert(abi.encodeWithSignature("IncorrectMintAmount()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 11, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowIncorrectNumberOfTokenIds() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC1155PresetMinterPauser burnToken = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnToken.mint(address(tokenRecipient), 1, 1000, ""); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + // instructions length != tokenIds length + vm.startPrank(tokenRecipient); + burnToken.setApprovalForAll(address(redeemMinter), true); + uint256[][] memory tokenIds = new uint256[][](2); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + vm.expectRevert(abi.encodeWithSignature("IncorrectNumberOfTokenIds()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](1); + tokenIds[0][0] = 1; + + // ERC1155: amounts length != tokenIds length + amounts = new uint256[][](2); + vm.expectRevert(abi.encodeWithSignature("IncorrectNumberOfTokenIds()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + } + + function test_MintFlowIncorrectBurnOrTransferAmount() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + address redeemTokenRecipient = address(323); + + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(tokenRecipient), 1, 500, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(tokenRecipient)); + burnTokenERC721.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](2); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 300, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC1155), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + instructions[1] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 3, + tokenIdStart: 0, + tokenIdEnd: 2, + tokenContract: address(burnTokenERC721), + transferRecipient: redeemTokenRecipient, + burnFunction: 0 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](2); + uint256[][] memory amounts = new uint256[][](2); + + // this would be correct + tokenIds[0] = new uint256[](1); + tokenIds[0][0] = 1; + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + tokenIds[1] = new uint256[](2); + tokenIds[1][0] = 0; + tokenIds[1][1] = 1; + + // ERC721: tokenids length != instruction amount + tokenIds[1] = new uint256[](1); + vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[1] = new uint256[](3); + vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + tokenIds[1] = new uint256[](2); + tokenIds[1][0] = 0; + tokenIds[1][1] = 1; + + // ERC1155: sum of amounts != instruction amount + amounts[0][0] = 499; + vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + amounts[0][0] = 501; + vm.expectRevert(abi.encodeWithSignature("IncorrectBurnOrTransferAmount()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerBurn20() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnTokenERC20), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnFrom(address,uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC20.approve(address(redeemMinter), 500); + + uint256[][] memory tokenIds = new uint256[][](1); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + vm.expectRevert(abi.encodeWithSignature("BurnFailed()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerBurn1155() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC1155), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burnBatch(address,uint256[],uint256[])"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](1); + tokenIds[0][0] = 1; + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + vm.expectRevert(abi.encodeWithSignature("BurnFailed()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerBurn721() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC721), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.prank(actualTokenOwner); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + vm.startPrank(tokenRecipient); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](2); + tokenIds[0][0] = 0; + tokenIds[0][1] = 1; + uint256[][] memory amounts; + + vm.expectRevert(abi.encodeWithSignature("SenderIsNotTokenOwner()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerTransfer20() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC20, + amount: 500, + tokenIdStart: 0, + tokenIdEnd: 0, + tokenContract: address(burnTokenERC20), + transferRecipient: address(1), + burnFunction: bytes4(0) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC20.approve(address(redeemMinter), 500); + + uint256[][] memory tokenIds = new uint256[][](1); + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + vm.expectRevert("ERC20: transfer amount exceeds balance"); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerTransfer1155() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155, + amount: 500, + tokenIdStart: 1, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC1155), + transferRecipient: address(1), + burnFunction: bytes4(0) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC1155.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](1); + tokenIds[0][0] = 1; + uint256[][] memory amounts = new uint256[][](1); + amounts[0] = new uint256[](1); + amounts[0][0] = 500; + + vm.expectRevert("ERC1155: insufficient balance for transfer"); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + function test_MintFlowSenderNotTokenOwnerTransfer721() external { + vm.startPrank(admin); + + address actualTokenOwner = address(92834); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC20PresetMinterPauser burnTokenERC20 = new ERC20PresetMinterPauser("Random Token", "RAND"); + burnTokenERC20.mint(address(actualTokenOwner), 1000); + ERC1155PresetMinterPauser burnTokenERC1155 = new ERC1155PresetMinterPauser("https://zora.co/testing/token.json"); + burnTokenERC1155.mint(address(actualTokenOwner), 1, 1000, ""); + ERC721PresetMinterPauserAutoId burnTokenERC721 = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnTokenERC721.mint(address(actualTokenOwner)); + burnTokenERC721.mint(address(actualTokenOwner)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnTokenERC721), + transferRecipient: address(1), + burnFunction: bytes4(0) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnTokenERC721.setApprovalForAll(address(redeemMinter), true); + + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](2); + tokenIds[0][0] = 0; + tokenIds[0][1] = 1; + uint256[][] memory amounts; + + vm.expectRevert("ERC721: caller is not token owner or approved"); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + vm.stopPrank(); + } + + ///////// RESET AND CLEAR ///////// + + function test_ResetSaleAlwaysReverts() external { + vm.prank(address(target)); + vm.expectRevert(abi.encodeWithSignature("MustCallClearRedeem()")); + redeemMinter.resetSale(uint256(1)); + } + + function test_ClearRedeem() external { + vm.startPrank(admin); + + address tokenRecipient = address(322); + vm.deal(tokenRecipient, 20 ether); + + ERC721PresetMinterPauserAutoId burnToken = new ERC721PresetMinterPauserAutoId("Test token", "TEST", "https://zora.co/testing/token.json"); + burnToken.mint(address(tokenRecipient)); + burnToken.mint(address(tokenRecipient)); + + ZoraCreatorRedeemMinterStrategy.MintToken memory mintToken = ZoraCreatorRedeemMinterStrategy.MintToken({ + tokenContract: address(target), + tokenId: newTokenId, + amount: 10, + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC1155 + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstruction[] memory instructions = new ZoraCreatorRedeemMinterStrategy.RedeemInstruction[](1); + instructions[0] = ZoraCreatorRedeemMinterStrategy.RedeemInstruction({ + tokenType: ZoraCreatorRedeemMinterStrategy.TokenType.ERC721, + amount: 2, + tokenIdStart: 0, + tokenIdEnd: 1, + tokenContract: address(burnToken), + transferRecipient: address(0), + burnFunction: bytes4(keccak256(bytes("burn(uint256)"))) + }); + ZoraCreatorRedeemMinterStrategy.RedeemInstructions memory redeemInstructions = ZoraCreatorRedeemMinterStrategy.RedeemInstructions({ + mintToken: mintToken, + instructions: instructions, + saleStart: 0, + saleEnd: type(uint64).max, + ethAmount: 1 ether, + ethRecipient: address(0) + }); + + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.setRedeem.selector, newTokenId, redeemInstructions)); + bytes32[] memory hashes = new bytes32[](1); + hashes[0] = keccak256(abi.encode(redeemInstructions)); + vm.expectEmit(true, false, false, true); + emit RedeemsCleared(address(target), hashes); + target.callSale(newTokenId, redeemMinter, abi.encodeWithSelector(ZoraCreatorRedeemMinterStrategy.clearRedeem.selector, newTokenId, hashes)); + vm.stopPrank(); + + vm.startPrank(tokenRecipient); + burnToken.setApprovalForAll(address(redeemMinter), true); + uint256[][] memory tokenIds = new uint256[][](1); + tokenIds[0] = new uint256[](2); + tokenIds[0][0] = 0; + tokenIds[0][1] = 1; + uint256[][] memory amounts = new uint256[][](1); + vm.expectRevert(abi.encodeWithSignature("RedeemInstructionNotAllowed()")); + target.mint{value: 1 ether}(redeemMinter, newTokenId, 10, abi.encode(redeemInstructions, tokenIds, amounts)); + } +}