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

supply royalty #118

Merged
merged 10 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .storage-layout
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
| config | struct IERC721Drop.Configuration | 352 | 0 | 64 | src/ERC721Drop.sol:ERC721Drop |
| salesConfig | struct IERC721Drop.SalesConfiguration | 354 | 0 | 96 | src/ERC721Drop.sol:ERC721Drop |
| presaleMintsByAddress | mapping(address => uint256) | 357 | 0 | 32 | src/ERC721Drop.sol:ERC721Drop |
| royaltyMintSchedule | uint32 | 358 | 0 | 4 | src/ERC721Drop.sol:ERC721Drop |

=======================
➡ ERC721DropProxy
Expand Down
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[profile.default]
solc_version = '0.8.17'
optimizer = true
optimizer_runs = 5000
optimizer_runs = 3200
via_ir = true
out = 'dist/artifacts'
test = 'test'
Expand Down
47 changes: 42 additions & 5 deletions src/ERC721Drop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import {FundsReceiver} from "./utils/FundsReceiver.sol";
import {Version} from "./utils/Version.sol";
import {PublicMulticall} from "./utils/PublicMulticall.sol";
import {ERC721DropStorageV1} from "./storage/ERC721DropStorageV1.sol";
import {ERC721DropStorageV2} from "./storage/ERC721DropStorageV2.sol";


/**
* @notice ZORA NFT Base contract for Drops and Editions
Expand All @@ -56,7 +58,8 @@ contract ERC721Drop is
OwnableSkeleton,
FundsReceiver,
Version(12),
ERC721DropStorageV1
ERC721DropStorageV1,
ERC721DropStorageV2
{
/// @dev This is the max mint batch size for the optimized ERC721A mint contract
uint256 internal immutable MAX_MINT_BATCH_SIZE = 8;
Expand Down Expand Up @@ -440,7 +443,6 @@ contract ERC721Drop is
external
payable
nonReentrant
canMintTokens(quantity)
onlyPublicSaleActive
returns (uint256)
{
Expand All @@ -455,14 +457,16 @@ contract ERC721Drop is
external
payable
nonReentrant
canMintTokens(quantity)
onlyPublicSaleActive
returns (uint256)
{
return _handlePurchase(quantity, comment);
}

function _handlePurchase(uint256 quantity, string memory comment) internal returns (uint256) {
_handleSupplyRoyalty(quantity);
_requireCanMintQuantity(quantity);

uint256 salePrice = salesConfig.publicSalePrice;

if (msg.value != (salePrice + ZORA_MINT_FEE) * quantity) {
Expand Down Expand Up @@ -594,7 +598,6 @@ contract ERC721Drop is
external
payable
nonReentrant
canMintTokens(quantity)
onlyPresaleActive
returns (uint256)
{
Expand All @@ -617,7 +620,6 @@ contract ERC721Drop is
external
payable
nonReentrant
canMintTokens(quantity)
onlyPresaleActive
returns (uint256)
{
Expand All @@ -631,6 +633,9 @@ contract ERC721Drop is
bytes32[] calldata merkleProof,
string memory comment
) internal returns (uint256) {
_handleSupplyRoyalty(quantity);
_requireCanMintQuantity(quantity);

if (
!MerkleProofUpgradeable.verify(
merkleProof,
Expand Down Expand Up @@ -1267,6 +1272,38 @@ contract ERC721Drop is
emit MintFeePayout(zoraFee, ZORA_MINT_FEE_RECIPIENT, success);
}

function _requireCanMintQuantity(uint256 quantity) internal view {
if (quantity + _totalMinted() > config.editionSize) {
revert Mint_SoldOut();
}
}

function _handleSupplyRoyalty(uint256 mintAmount) internal returns (uint256 totalRoyaltyMints) {
jgeary marked this conversation as resolved.
Show resolved Hide resolved
if (royaltyMintSchedule == 0) {
// If we still have no schedule, return 0 supply royalty.
return 0;
}

totalRoyaltyMints = (mintAmount + (_totalMinted() % royaltyMintSchedule)) / (royaltyMintSchedule - 1);
jgeary marked this conversation as resolved.
Show resolved Hide resolved

if (totalRoyaltyMints > 0) {
address royaltyRecipient = config.fundsRecipient;

// If we have no recipient set, return 0 supply royalty.
if (royaltyRecipient == address(0)) {
jgeary marked this conversation as resolved.
Show resolved Hide resolved
return 0;
}
_mintNFTs(royaltyRecipient, totalRoyaltyMints);
jgeary marked this conversation as resolved.
Show resolved Hide resolved
}
}

function updateRoyaltyMintSchedule(uint32 newSchedule) external onlyAdmin {
jgeary marked this conversation as resolved.
Show resolved Hide resolved
if (newSchedule == 1) {
jgeary marked this conversation as resolved.
Show resolved Hide resolved
revert InvalidMintSchedule();
}
royaltyMintSchedule = newSchedule;
}

/// @notice ERC165 supports interface
/// @param interfaceId interface id to check if supported
function supportsInterface(bytes4 interfaceId)
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IERC721Drop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ interface IERC721Drop {
error Admin_InvalidUpgradeAddress(address proposedAddress);
/// @notice Unable to finalize an edition not marked as open (size set to uint64_max_value)
error Admin_UnableToFinalizeNotOpenEdition();
/// @notice Cannot reserve every mint for admin
error InvalidMintSchedule();

/// @notice Event emitted for mint fee payout
/// @param mintFeeAmount amount of the mint fee
Expand Down
6 changes: 6 additions & 0 deletions src/storage/ERC721DropStorageV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

contract ERC721DropStorageV2 {
uint32 public royaltyMintSchedule;
}
43 changes: 43 additions & 0 deletions test/ERC721Drop.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,49 @@ contract ERC721DropTest is Test {
assertEq(dummyRenderer.someState(), "");
}

function test_SupplyRoyaltyPurchase() public setupZoraNFTBase(100) {
jgeary marked this conversation as resolved.
Show resolved Hide resolved
vm.startPrank(DEFAULT_OWNER_ADDRESS);
vm.expectRevert(IERC721Drop.InvalidMintSchedule.selector);
zoraNFTBase.updateRoyaltyMintSchedule(1);

zoraNFTBase.updateRoyaltyMintSchedule(5);
jgeary marked this conversation as resolved.
Show resolved Hide resolved

zoraNFTBase.setSaleConfiguration({
publicSaleStart: 0,
publicSaleEnd: type(uint64).max,
presaleStart: 0,
presaleEnd: 0,
publicSalePrice: 0.1 ether,
maxSalePurchasePerAddress: 100,
presaleMerkleRoot: bytes32(0)
});
vm.stopPrank();

(, uint256 zoraFee) = zoraNFTBase.zoraFeeForAmount(9);
uint256 paymentAmount = 0.9 ether + zoraFee;
jgeary marked this conversation as resolved.
Show resolved Hide resolved
vm.deal(address(456), 200 ether);

vm.startPrank(address(456));
zoraNFTBase.purchase{value: paymentAmount}(9);

assertEq(zoraNFTBase.balanceOf(address(456)), 9);
assertEq(zoraNFTBase.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 2);

(, zoraFee) = zoraNFTBase.zoraFeeForAmount(72);
paymentAmount = 0.1 ether * 72 + zoraFee;
vm.expectRevert(IERC721Drop.Mint_SoldOut.selector);
zoraNFTBase.purchase{value: paymentAmount}(72);

(, zoraFee) = zoraNFTBase.zoraFeeForAmount(71);
paymentAmount = 0.1 ether * 71 + zoraFee;
zoraNFTBase.purchase{value: paymentAmount}(71);

assertEq(zoraNFTBase.balanceOf(address(456)), 80);
assertEq(zoraNFTBase.balanceOf(DEFAULT_FUNDS_RECIPIENT_ADDRESS), 20);
jgeary marked this conversation as resolved.
Show resolved Hide resolved

vm.stopPrank();
}

function test_EIP165() public view {
require(zoraNFTBase.supportsInterface(0x01ffc9a7), "supports 165");
require(zoraNFTBase.supportsInterface(0x80ac58cd), "supports 721");
Expand Down