Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: purchase with recipient #123

Merged
merged 5 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/ERC721Drop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ contract ERC721Drop is
onlyPublicSaleActive
returns (uint256)
{
return _handlePurchase(quantity, "");
return _handlePurchase(msg.sender, quantity, "");
}

/// @notice Purchase a quantity of tokens with a comment
Expand All @@ -463,10 +463,25 @@ contract ERC721Drop is
onlyPublicSaleActive
returns (uint256)
{
return _handlePurchase(quantity, comment);
return _handlePurchase(msg.sender, quantity, comment);
}

function _handlePurchase(uint256 quantity, string memory comment) internal returns (uint256) {
/// @notice Purchase a quantity of tokens to a specified recipient, with an optional comment
/// @param recipient recipient of the tokens
/// @param quantity quantity to purchase
/// @param comment optional comment to include in the IERC721Drop.Sale event (leave blank for no comment)
/// @return tokenId of the first token minted
function purchaseWithRecipient(address recipient, uint256 quantity, string calldata comment)
external
payable
nonReentrant
onlyPublicSaleActive
returns (uint256)
{
return _handlePurchase(recipient, quantity, comment);
}

function _handlePurchase(address recipient, uint256 quantity, string memory comment) internal returns (uint256) {
_mintSupplyRoyalty(quantity);
_requireCanMintQuantity(quantity);

Expand All @@ -480,21 +495,21 @@ contract ERC721Drop is
// Any other number, the per address mint limit is that.
if (
salesConfig.maxSalePurchasePerAddress != 0 &&
_numberMinted(_msgSender()) +
_numberMinted(recipient) +
quantity -
presaleMintsByAddress[_msgSender()] >
presaleMintsByAddress[recipient] >
salesConfig.maxSalePurchasePerAddress
) {
revert Purchase_TooManyForAddress();
}

_mintNFTs(_msgSender(), quantity);
_mintNFTs(recipient, quantity);
uint256 firstMintedTokenId = _lastMintedTokenId() - quantity;

_payoutZoraFee(quantity);

emit IERC721Drop.Sale({
to: _msgSender(),
to: recipient,
quantity: quantity,
pricePerToken: salePrice,
firstPurchasedTokenId: firstMintedTokenId
Expand Down
88 changes: 88 additions & 0 deletions test/ERC721Drop.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,94 @@ contract ERC721DropTest is Test {
zoraNFTBase.purchaseWithComment{value: paymentAmount}(purchaseQuantity, "test comment");
}

function test_PurchaseWithRecipient(uint64 salePrice, 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: salePrice,
maxSalePurchasePerAddress: purchaseQuantity + 1,
presaleMerkleRoot: bytes32(0)
});

(, uint256 zoraFee) = zoraNFTBase.zoraFeeForAmount(purchaseQuantity);
uint256 paymentAmount = uint256(salePrice) * purchaseQuantity + zoraFee;

address minter = makeAddr("minter");
address recipient = makeAddr("recipient");

vm.deal(minter, paymentAmount);
vm.prank(minter);
zoraNFTBase.purchaseWithRecipient{value: paymentAmount}(recipient, purchaseQuantity, "");
kulkarohan marked this conversation as resolved.
Show resolved Hide resolved

for (uint256 i; i < purchaseQuantity; ) {
assertEq(zoraNFTBase.ownerOf(++i), recipient);
}
}

function test_PurchaseWithRecipientAndComment(uint64 salePrice, 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: salePrice,
maxSalePurchasePerAddress: purchaseQuantity + 1,
presaleMerkleRoot: bytes32(0)
});

(, uint256 zoraFee) = zoraNFTBase.zoraFeeForAmount(purchaseQuantity);
uint256 paymentAmount = uint256(salePrice) * purchaseQuantity + zoraFee;

address minter = makeAddr("minter");
address recipient = makeAddr("recipient");

vm.deal(minter, paymentAmount);

vm.expectEmit(true, true, true, true);
emit MintComment(
minter,
address(zoraNFTBase),
0,
purchaseQuantity,
"test comment"
);
vm.prank(minter);
zoraNFTBase.purchaseWithRecipient{value: paymentAmount}(recipient, purchaseQuantity, "test comment");
}

function testRevert_PurchaseWithInvalidRecipient(uint64 salePrice, 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: salePrice,
maxSalePurchasePerAddress: purchaseQuantity + 1,
presaleMerkleRoot: bytes32(0)
});

(, uint256 zoraFee) = zoraNFTBase.zoraFeeForAmount(purchaseQuantity);
uint256 paymentAmount = uint256(salePrice) * purchaseQuantity + zoraFee;


address minter = makeAddr("minter");
address recipient = address(0);

vm.deal(minter, paymentAmount);

vm.expectRevert(abi.encodeWithSignature("MintToZeroAddress()"));
vm.prank(minter);
zoraNFTBase.purchaseWithRecipient{value: paymentAmount}(recipient, purchaseQuantity, "");
}

function test_UpgradeApproved() public setupZoraNFTBase(10) {
address newImpl = address(
new ERC721Drop(
Expand Down