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: creator reward recipient per token #278

Merged
merged 14 commits into from
Oct 25, 2023
21 changes: 16 additions & 5 deletions packages/1155-contracts/src/nft/ZoraCreator1155Impl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ contract ZoraCreator1155Impl is
uint256 ethValueSent = _handleRewardsAndGetValueSent(
msg.value,
quantity,
getCreatorRewardRecipient(),
getCreatorRewardRecipient(tokenId),
createReferrals[tokenId],
address(0),
firstMinters[tokenId]
Expand Down Expand Up @@ -437,7 +437,7 @@ contract ZoraCreator1155Impl is
uint256 ethValueSent = _handleRewardsAndGetValueSent(
msg.value,
quantity,
getCreatorRewardRecipient(),
getCreatorRewardRecipient(tokenId),
createReferrals[tokenId],
mintReferral,
firstMinters[tokenId]
Expand All @@ -454,9 +454,20 @@ contract ZoraCreator1155Impl is
}

/// @notice Get the creator reward recipient address
/// @dev The creator is not enforced to set a funds recipient address, so in that case the reward would be claimable by creator's contract
function getCreatorRewardRecipient() public view returns (address payable) {
return config.fundsRecipient != address(0) ? config.fundsRecipient : payable(address(this));
/// @param tokenId The token id to get the creator reward recipient for
/// @dev The creator is not enforced to set a funds recipient address for a specific token or contract-wide,
/// so in the case of both the reward recipient is set to the creator's contract,
/// which can be withdrawn by the creator via the `withdrawRewards` function.
function getCreatorRewardRecipient(uint256 tokenId) public view returns (address) {
if (getRoyalties(tokenId).royaltyRecipient != address(0)) {
kulkarohan marked this conversation as resolved.
Show resolved Hide resolved
return getRoyalties(tokenId).royaltyRecipient;
}

if (config.fundsRecipient != address(0)) {
return config.fundsRecipient;
}

return address(this);
}

/// @notice Set a metadata renderer for a token
Expand Down
125 changes: 125 additions & 0 deletions packages/1155-contracts/test/nft/ZoraCreator1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,19 @@ contract ZoraCreator1155Test is Test {
address internal zora;

event Purchased(address indexed sender, address indexed minter, uint256 indexed tokenId, uint256 quantity, uint256 value);
event RewardsDeposit(
address indexed creator,
address indexed createReferral,
address indexed mintReferral,
address firstMinter,
address zora,
address from,
uint256 creatorReward,
uint256 createReferralReward,
uint256 mintReferralReward,
uint256 firstMinterReward,
uint256 zoraReward
);

function setUp() external {
creator = makeAddr("creator");
Expand Down Expand Up @@ -1135,6 +1148,118 @@ contract ZoraCreator1155Test is Test {
assertEq(protocolRewards.balanceOf(zora), settings.zoraReward + settings.createReferralReward);
}

function test_SetCreatorRewardRecipientForToken() public {
address collaborator = makeAddr("collaborator");
uint256 quantity = 100;

init();

vm.prank(admin);
uint256 tokenId = target.setupNewToken("test", quantity);

address creatorRewardRecipient;

creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);

ICreatorRoyaltiesControl.RoyaltyConfiguration memory newRoyaltyConfig = ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, collaborator);

vm.prank(admin);
target.updateRoyaltiesForToken(tokenId, newRoyaltyConfig);

creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);

assertEq(creatorRewardRecipient, collaborator);

vm.prank(admin);
target.addPermission(tokenId, address(simpleMinter), adminRole);

RewardsSettings memory settings = target.computeFreeMintRewards(quantity);

uint256 totalReward = target.computeTotalReward(quantity);
vm.deal(collector, totalReward);

vm.prank(collector);
vm.expectEmit(true, true, true, true);
emit RewardsDeposit(
collaborator,
zora,
zora,
collaborator,
zora,
address(target),
settings.creatorReward,
settings.createReferralReward,
settings.mintReferralReward,
settings.firstMinterReward,
settings.zoraReward
);
target.mintWithRewards{value: totalReward}(simpleMinter, tokenId, quantity, abi.encode(recipient), address(0));
kulkarohan marked this conversation as resolved.
Show resolved Hide resolved

assertEq(protocolRewards.balanceOf(collaborator), settings.creatorReward + settings.firstMinterReward);
}

function test_CreatorRewardRecipientConditionalAddress() public {
ICreatorRoyaltiesControl.RoyaltyConfiguration memory royaltyConfig;
address creatorRewardRecipient;

address collaborator = makeAddr("collaborator");
uint256 quantity = 100;

init();

vm.prank(admin);
uint256 tokenId = target.setupNewToken("test", quantity);

(, , address contractFundsRecipient, , , ) = target.config();

creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);
assertEq(creatorRewardRecipient, contractFundsRecipient);

royaltyConfig = ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, collaborator);
vm.prank(admin);
target.updateRoyaltiesForToken(tokenId, royaltyConfig);

creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);
assertEq(creatorRewardRecipient, collaborator);

royaltyConfig = ICreatorRoyaltiesControl.RoyaltyConfiguration(0, 0, address(0));
vm.prank(admin);
target.updateRoyaltiesForToken(tokenId, royaltyConfig);

vm.prank(admin);
target.setFundsRecipient(payable(address(0)));

creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);
assertEq(creatorRewardRecipient, address(target));
}

function test_ContractAsCreatorRewardRecipientFallback() public {
uint256 quantity = 100;

init();

vm.startPrank(admin);
uint256 tokenId = target.setupNewToken("test", quantity);

target.setFundsRecipient(payable(address(0)));

target.addPermission(tokenId, address(simpleMinter), adminRole);
vm.stopPrank();

RewardsSettings memory settings = target.computeFreeMintRewards(quantity);

uint256 totalReward = target.computeTotalReward(quantity);
vm.deal(collector, totalReward);

address creatorRewardRecipient = target.getCreatorRewardRecipient(tokenId);

vm.prank(collector);
target.mintWithRewards{value: totalReward}(simpleMinter, tokenId, quantity, abi.encode(recipient), address(0));

assertEq(creatorRewardRecipient, address(target));
assertEq(protocolRewards.balanceOf(address(target)), settings.creatorReward + settings.firstMinterReward);
kulkarohan marked this conversation as resolved.
Show resolved Hide resolved
}

function testRevert_WrongValueForSale(uint256 quantity, uint256 salePrice) public {
vm.assume(quantity > 0 && quantity < 1_000_000);
vm.assume(salePrice > 0 && salePrice < 10 ether);
Expand Down
Loading