From f47b8f208ee27938b187dbbbba58c0eb33676ecb Mon Sep 17 00:00:00 2001 From: iain nash Date: Fri, 22 Sep 2023 15:21:05 -0400 Subject: [PATCH 1/3] Propose change fixes --- test/ERC721Drop.t.sol | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/test/ERC721Drop.t.sol b/test/ERC721Drop.t.sol index 9ea9de1..0c63043 100644 --- a/test/ERC721Drop.t.sol +++ b/test/ERC721Drop.t.sol @@ -206,7 +206,38 @@ contract ERC721DropTest is Test { assertEq(royaltyAmount, 0 ether); } - function test_Purchase(uint64 salePrice, uint32 purchaseQuantity) public setupZoraNFTBase(purchaseQuantity) { + function test_PurchaseFreeMint(uint32 purchaseQuantity) public setupZoraNFTBase(purchaseQuantity) { + vm.assume(purchaseQuantity < 100 && purchaseQuantity > 0); + vm.prank(DEFAULT_OWNER_ADDRESS); + zoraNFTBase.setSaleConfiguration({ + publicSaleStart: 0, + publicSaleEnd: type(uint64).max, + presaleStart: 0, + presaleEnd: 0, + publicSalePrice: 0, + maxSalePurchasePerAddress: purchaseQuantity + 1, + presaleMerkleRoot: bytes32(0) + }); + + (, uint256 protocolFee) = zoraNFTBase.zoraFeeForAmount(purchaseQuantity); + uint256 paymentAmount = protocolFee; + vm.deal(address(456), paymentAmount); + vm.prank(address(456)); + vm.expectEmit(true, true, true, true); + emit Sale(address(456), purchaseQuantity, 0, 0); + zoraNFTBase.purchase{value: paymentAmount}(purchaseQuantity); + + assertEq(zoraNFTBase.saleDetails().maxSupply, purchaseQuantity); + assertEq(zoraNFTBase.saleDetails().totalMinted, purchaseQuantity); + require(zoraNFTBase.ownerOf(1) == address(456), "owner is wrong for new minted token"); + assertEq(address(zoraNFTBase).balance, paymentAmount - protocolFee); + assertEq(address(protocolRewards).balance, protocolFee); + assertEq(protocolRewards.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 0.000444 ether * purchaseQuantity); + assertEq(protocolRewards.balanceOf(mintFeeRecipient), 0.000333 ether * purchaseQuantity); + } + + function test_PurchaseWithValue(uint64 salePrice, uint32 purchaseQuantity) public setupZoraNFTBase(purchaseQuantity) { + vm.assume(salePrice > 0); vm.assume(purchaseQuantity < 100 && purchaseQuantity > 0); vm.prank(DEFAULT_OWNER_ADDRESS); zoraNFTBase.setSaleConfiguration({ @@ -230,10 +261,12 @@ contract ERC721DropTest is Test { assertEq(zoraNFTBase.saleDetails().maxSupply, purchaseQuantity); assertEq(zoraNFTBase.saleDetails().totalMinted, purchaseQuantity); require(zoraNFTBase.ownerOf(1) == address(456), "owner is wrong for new minted token"); + assertEq(address(zoraNFTBase).balance, paymentAmount - zoraFee); assertEq(address(protocolRewards).balance, zoraFee); - assertEq(protocolRewards.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 0.000444 ether * purchaseQuantity); - assertEq(protocolRewards.balanceOf(mintFeeRecipient), 0.000333 ether * purchaseQuantity); + assertEq(protocolRewards.balanceOf(mintFeeRecipient), 0.000666 ether * purchaseQuantity); + assertEq(address(zoraNFTBase).balance, uint256(salePrice) * uint256(purchaseQuantity)); + assertEq(protocolRewards.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 0.000111 ether * uint256(purchaseQuantity)); } function test_PurchaseWithComment(uint64 salePrice, uint32 purchaseQuantity) public setupZoraNFTBase(purchaseQuantity) { From f92430e5719e82e8ed34452307e6e713ed0d8b07 Mon Sep 17 00:00:00 2001 From: Isabella Smallcombe Date: Fri, 22 Sep 2023 14:09:54 -0400 Subject: [PATCH 2/3] feat: remove old purchase without rewards functions --- src/ERC721Drop.sol | 87 ++++-------------------------------- test/ERC721Drop.t.sol | 11 ++--- test/merkle/MerkleDrop.t.sol | 3 +- 3 files changed, 16 insertions(+), 85 deletions(-) diff --git a/src/ERC721Drop.sol b/src/ERC721Drop.sol index 826fa2e..5a5085a 100644 --- a/src/ERC721Drop.sol +++ b/src/ERC721Drop.sol @@ -452,7 +452,7 @@ contract ERC721Drop is onlyPublicSaleActive returns (uint256) { - return _handlePurchase(msg.sender, quantity, ""); + return _handleMintWithRewards(msg.sender, quantity, "", address(0)); } /// @notice Purchase a quantity of tokens with a comment @@ -466,7 +466,7 @@ contract ERC721Drop is onlyPublicSaleActive returns (uint256) { - return _handlePurchase(msg.sender, quantity, comment); + return _handleMintWithRewards(msg.sender, quantity, comment, address(0)); } /// @notice Purchase a quantity of tokens to a specified recipient, with an optional comment @@ -481,7 +481,7 @@ contract ERC721Drop is onlyPublicSaleActive returns (uint256) { - return _handlePurchase(recipient, quantity, comment); + return _handleMintWithRewards(recipient, quantity, comment, address(0)); } /// @notice Mint a quantity of tokens with a comment that will pay out rewards @@ -501,7 +501,7 @@ contract ERC721Drop is return _handleMintWithRewards(recipient, quantity, comment, mintReferral); } - function _handleMintWithRewards(address recipient, uint256 quantity, string calldata comment, address mintReferral) internal returns (uint256) { + function _handleMintWithRewards(address recipient, uint256 quantity, string memory comment, address mintReferral) internal returns (uint256) { _mintSupplyRoyalty(quantity); _requireCanPurchaseQuantity(recipient, quantity); @@ -518,25 +518,6 @@ contract ERC721Drop is return firstMintedTokenId; } - function _handlePurchase(address recipient, uint256 quantity, string memory comment) internal returns (uint256) { - _mintSupplyRoyalty(quantity); - _requireCanMintQuantity(quantity); - _requireCanPurchaseQuantity(recipient, quantity); - - uint256 salePrice = salesConfig.publicSalePrice; - - _requireLegacyFee(msg.value, salePrice, quantity); - - _mintNFTs(recipient, quantity); - uint256 firstMintedTokenId = _lastMintedTokenId() - quantity; - - _payoutZoraFee(quantity); - - _emitSaleEvents(_msgSender(), recipient, quantity, salePrice, firstMintedTokenId, comment); - - return firstMintedTokenId; - } - /// @notice Function to mint NFTs /// @dev (important: Does not enforce max supply limit, enforce that limit earlier) /// @dev This batches in size of 8 as per recommended by ERC721A creators @@ -626,11 +607,9 @@ contract ERC721Drop is ) external payable - nonReentrant - onlyPresaleActive returns (uint256) { - return _handlePurchasePresale(quantity, maxQuantity, pricePerToken, merkleProof, ""); + return purchasePresaleWithRewards(quantity, maxQuantity, pricePerToken, merkleProof, "", address(0)); } /// @notice Merkle-tree based presale purchase function with a comment @@ -652,49 +631,7 @@ contract ERC721Drop is onlyPresaleActive returns (uint256) { - return _handlePurchasePresale(quantity, maxQuantity, pricePerToken, merkleProof, comment); - } - - function _handlePurchasePresale( - uint256 quantity, - uint256 maxQuantity, - uint256 pricePerToken, - bytes32[] calldata merkleProof, - string memory comment - ) internal returns (uint256) { - _mintSupplyRoyalty(quantity); - _requireCanMintQuantity(quantity); - - address msgSender = _msgSender(); - - _requireMerkleApproval(msgSender, maxQuantity, pricePerToken, merkleProof); - - _requireLegacyFee(msg.value, pricePerToken, quantity); - - _requireCanPurchasePresale(msgSender, quantity, maxQuantity); - - _mintNFTs(msgSender, quantity); - uint256 firstMintedTokenId = _lastMintedTokenId() - quantity; - - _payoutZoraFee(quantity); - - emit IERC721Drop.Sale({ - to: msgSender, - quantity: quantity, - pricePerToken: pricePerToken, - firstPurchasedTokenId: firstMintedTokenId - }); - if (bytes(comment).length > 0) { - emit IERC721Drop.MintComment({ - sender: msgSender, - tokenContract: address(this), - tokenId: firstMintedTokenId, - quantity: quantity, - comment: comment - }); - } - - return firstMintedTokenId; + return purchasePresaleWithRewards(quantity, maxQuantity, pricePerToken, merkleProof, comment, address(0)); } /// @notice Merkle-tree based presale purchase function with a comment and protocol rewards @@ -709,10 +646,10 @@ contract ERC721Drop is uint256 maxQuantity, uint256 pricePerToken, bytes32[] calldata merkleProof, - string calldata comment, + string memory comment, address mintReferral ) - external + public payable nonReentrant onlyPresaleActive @@ -726,7 +663,7 @@ contract ERC721Drop is uint256 maxQuantity, uint256 pricePerToken, bytes32[] calldata merkleProof, - string calldata comment, + string memory comment, address mintReferral ) internal returns (uint256) { _mintSupplyRoyalty(quantity); @@ -1281,12 +1218,6 @@ contract ERC721Drop is emit MintFeePayout(zoraFee, ZORA_MINT_FEE_RECIPIENT, success); } - function _requireLegacyFee(uint256 msgValue, uint256 salePrice, uint256 quantity) internal view { - if (msgValue != (salePrice + ZORA_MINT_FEE) * quantity) { - revert Purchase_WrongPrice((salePrice + ZORA_MINT_FEE) * quantity); - } - } - function _requireCanMintQuantity(uint256 quantity) internal view { if (quantity + _totalMinted() > config.editionSize) { revert Mint_SoldOut(); diff --git a/test/ERC721Drop.t.sol b/test/ERC721Drop.t.sol index d57fd9b..9ea9de1 100644 --- a/test/ERC721Drop.t.sol +++ b/test/ERC721Drop.t.sol @@ -231,7 +231,9 @@ contract ERC721DropTest is Test { assertEq(zoraNFTBase.saleDetails().totalMinted, purchaseQuantity); require(zoraNFTBase.ownerOf(1) == address(456), "owner is wrong for new minted token"); assertEq(address(zoraNFTBase).balance, paymentAmount - zoraFee); - assertEq(mintFeeRecipient.balance, zoraFee); + assertEq(address(protocolRewards).balance, zoraFee); + assertEq(protocolRewards.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 0.000444 ether * purchaseQuantity); + assertEq(protocolRewards.balanceOf(mintFeeRecipient), 0.000333 ether * purchaseQuantity); } function test_PurchaseWithComment(uint64 salePrice, uint32 purchaseQuantity) public setupZoraNFTBase(purchaseQuantity) { @@ -787,9 +789,8 @@ contract ERC721DropTest is Test { maxSalePurchasePerAddress: 2, presaleMerkleRoot: bytes32(0) }); - (, uint256 fee) = zoraNFTBase.zoraFeeForAmount(1); vm.prank(address(456)); - vm.expectRevert(abi.encodeWithSelector(IERC721Drop.Purchase_WrongPrice.selector, 0.15 ether + fee)); + vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); zoraNFTBase.purchase{value: 0.12 ether}(1); } @@ -940,7 +941,7 @@ contract ERC721DropTest is Test { vm.expectRevert(IERC721Drop.Mint_SoldOut.selector); vm.prank(DEFAULT_OWNER_ADDRESS); zoraNFTBase.adminMint(address(0x1234), 2); - vm.expectRevert(IERC721Drop.Mint_SoldOut.selector); + vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); zoraNFTBase.purchase{value: 0.6 ether}(3); } @@ -981,7 +982,7 @@ contract ERC721DropTest is Test { vm.deal(address(456), uint256(1) * 2); vm.prank(address(456)); - vm.expectRevert(IERC721Drop.Mint_SoldOut.selector); + vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); zoraNFTBase.purchase{value: 1}(1); } diff --git a/test/merkle/MerkleDrop.t.sol b/test/merkle/MerkleDrop.t.sol index 73a3b0f..3024d80 100644 --- a/test/merkle/MerkleDrop.t.sol +++ b/test/merkle/MerkleDrop.t.sol @@ -112,11 +112,10 @@ contract ZoraNFTBaseTest is Test { MerkleData.MerkleEntry memory item; item = merkleData.getTestSetByName("test-3-addresses").entries[0]; - (, uint256 fee) = zoraNFTBase.zoraFeeForAmount(1); vm.deal(address(item.user), 1 ether); vm.startPrank(address(item.user)); - vm.expectRevert(abi.encodeWithSelector(IERC721Drop.Purchase_WrongPrice.selector, item.mintPrice + fee)); + vm.expectRevert(abi.encodeWithSignature("INVALID_ETH_AMOUNT()")); zoraNFTBase.purchasePresale{value: item.mintPrice - 1}(1, item.maxMint, item.mintPrice, item.proof); assertEq(zoraNFTBase.saleDetails().maxSupply, 10); assertEq(zoraNFTBase.saleDetails().totalMinted, 0); From 4fa740498020c7f5a330bdc13c5168f16173829b Mon Sep 17 00:00:00 2001 From: Isabella Smallcombe Date: Mon, 25 Sep 2023 17:29:25 -0400 Subject: [PATCH 3/3] feat: add changeset --- .changeset/nervous-roses-decide.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nervous-roses-decide.md diff --git a/.changeset/nervous-roses-decide.md b/.changeset/nervous-roses-decide.md new file mode 100644 index 0000000..b333a45 --- /dev/null +++ b/.changeset/nervous-roses-decide.md @@ -0,0 +1,5 @@ +--- +'@zoralabs/nft-drop-contracts': minor +--- + +Removing old purchase without rewards functions for purchase and purchasePresale