Skip to content

Commit

Permalink
merge
Browse files Browse the repository at this point in the history
  • Loading branch information
iainnash committed Oct 30, 2023
2 parents 9135d2e + 7e9f3a3 commit a70a355
Show file tree
Hide file tree
Showing 17 changed files with 187 additions and 81 deletions.
5 changes: 5 additions & 0 deletions .changeset/weak-planets-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@zoralabs/zora-1155-contracts": minor
---

Creator reward recipient can now be defined on a token by token basis. This allows for multiple creators to collaborate on a contract and each to receive rewards for the token they created. The royaltyRecipient storage field is now used to determine the creator reward recipient for each token. If that's not set for a token, it falls back to use the contract wide fundsRecipient.
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/1.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0x8e90D8cfc0CA66EA143930E4c5F7E31Bf16F722b",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0x55B53DBE22859d538E3b44DD06C9FAE292409E3c",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/10.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0xF3a46845548bE811Ce37e65153563f4a0AaEbe31",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0xF7e49F97E82cc38ACd82E303F37Fe046f5a190B5",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x3678862f04290E565cCA2EF163BAeb92Bb76790C",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/420.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0xAF5A4F6F6640734d7D000321Bb27De40D4Ae91f6",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0x7B59c0378F540c0356A5DAEF7574255A7C74EC76",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/5.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0x455c9D3188A3Cd94aCDE8E5Ec90cA92FC10805EA",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0x9074Ae399235d26B56e3aF1331b033366E4FE072",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/7777777.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0xFc40F9BFE1289F27F89BAEfb4DB97CC2D8eF6a38",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0xC7598f8eAA1455f5b2B3f206A9af55B2BA248e3E",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/8453.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0x314E552b55DFbDfD4d76623E1D45E5056723998B",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0xC6899816663891D7493939d74d83cb7f2BBcBB16",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/addresses/84531.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"CONTRACT_1155_IMPL": "0xD66B730aA3B4921356Fc56907D22e65CA9F4ff58",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0xF482C51346f3c77673dc619F243Eb8B09E9A954E",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
Expand Down
7 changes: 4 additions & 3 deletions packages/1155-contracts/addresses/999.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"CONTRACT_1155_IMPL": "0x486709A6BeDBD8476d7bCF73F1ab42579A1A7d78",
"CONTRACT_1155_IMPL_VERSION": "2.0.0",
"FACTORY_IMPL": "0x366A36c10E1C851dcfA7804fB313dEA8E3488335",
"CONTRACT_1155_IMPL": "0xCE00c75B9807A2aA87B2297cA7Dc1C0190137D6F",
"CONTRACT_1155_IMPL_VERSION": "2.1.0",
"UPGRADE_GATE": "0xbC50029836A59A4E5e1Bb8988272F46ebA0F9900",
"FACTORY_IMPL": "0x15ba66e376856F3F6FE53dE9eeAb10dEF10E8C92",
"FACTORY_PROXY": "0x777777C338d93e2C7adf08D102d45CA7CC4Ed021",
"FIXED_PRICE_SALE_STRATEGY": "0x04E2516A2c207E84a1839755675dfd8eF6302F0a",
"MERKLE_MINT_SALE_STRATEGY": "0xf48172CA3B6068B20eE4917Eb27b5472f1f272C7",
Expand Down
7 changes: 6 additions & 1 deletion packages/1155-contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
[profile.default]
fs_permissions = [{access = "read", path = "./addresses"}, {access = "read", path = "./chainConfigs"}, {access = "read", path = "./package.json"}, {access = "readwrite", path = "./deterministicConfig"}]
fs_permissions = [
{ access = "read", path = "./addresses" },
{ access = "read", path = "./chainConfigs" },
{ access = "read", path = "./package.json" },
{ access = "readwrite", path = "./deterministicConfig" },
]
libs = ['_imagine', 'node_modules', 'script']
allow_paths = ["node_modules/@zoralabs/protocol-rewards"]
optimizer = true
Expand Down
8 changes: 6 additions & 2 deletions packages/1155-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,13 @@
"build": "tsup",
"build:sizes": "forge build --sizes",
"bundle-configs": "node script/bundle-chainConfigs.mjs && yarn prettier",
"wagmi": "wagmi generate",
"wagmi": "FOUNDRY_PROFILE=dev forge build && wagmi generate",
"storage-inspect:check": "./script/storage-check.sh check ZoraCreator1155Impl ZoraCreator1155FactoryImpl",
"storage-inspect:generate": "./script/storage-check.sh generate ZoraCreator1155Impl ZoraCreator1155FactoryImpl"
"storage-inspect:generate": "./script/storage-check.sh generate ZoraCreator1155Impl ZoraCreator1155FactoryImpl",
"js-test:watch": "vitest dev",
"anvil": "source .env.anvil && anvil --fork-url $FORK_RPC_URL --fork-block-number $FORK_BLOCK_NUMBER --chain-id 31337",
"unlink-contracts": "rm -rf ./node_modules/@zoralabs/protocol-rewards && cp -r ../protocol-rewards ./node_modules/@zoralabs/protocol-rewards",
"link-contracts": "rm -rf ./node_modules && cd .. && yarn"
},
"files": [
"dist/",
Expand Down
1 change: 1 addition & 0 deletions packages/1155-contracts/script/ZoraDeployerBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ abstract contract ZoraDeployerBase is ScriptDeploymentConfig, DeterministicDeplo
vm.serializeAddress(deploymentJsonKey, FACTORY_IMPL, deployment.factoryImpl);
vm.serializeAddress(deploymentJsonKey, PREMINTER_PROXY, deployment.preminterProxy);
vm.serializeAddress(deploymentJsonKey, PREMINTER_IMPL, deployment.preminterImpl);
vm.serializeAddress(deploymentJsonKey, UPGRADE_GATE, deployment.upgradeGate);
deploymentJson = vm.serializeAddress(deploymentJsonKey, FACTORY_PROXY, deployment.factoryProxy);
console2.log(deploymentJson);
}
Expand Down
25 changes: 19 additions & 6 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 @@ -453,10 +453,23 @@ contract ZoraCreator1155Impl is
return TOTAL_REWARD_PER_MINT;
}

/// @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));
/// @notice Get the creator reward recipient address for a specific token.
/// @param tokenId The token id to get the creator reward recipient for
/// @dev Returns the royalty recipient address for the token if set; otherwise uses the fundsRecipient.
/// If both are not set, this contract will be set as the recipient, and an account with
/// `PERMISSION_BIT_FUNDS_MANAGER` will be able to withdraw via the `withdrawRewards` function.
function getCreatorRewardRecipient(uint256 tokenId) public view returns (address) {
address royaltyRecipient = getRoyalties(tokenId).royaltyRecipient;

if (royaltyRecipient != address(0)) {
return royaltyRecipient;
}

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

return address(this);
}

/// @notice Set a metadata renderer for a token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,28 +149,4 @@ contract ZoraCreator1155FactoryForkTest is ForkDeploymentConfig, Test {
testTheFork(forkTestChains[i]);
}
}

// this is a temporary test to simulate the upgrade to the correct factory implementation
// on zora goerli. it can be deleted post upgrade
function test_fork_zoraGoerli_factoryUpgradeCanMint() external {
// create and select the fork, which will be used for all subsequent calls
// it will also affect the current block chain id based on the rpc url returned
vm.createSelectFork(vm.rpcUrl("zora_goerli"));

Deployment memory deployment = getDeployment();

address factoryAddress = deployment.factoryProxy;
ZoraCreator1155FactoryImpl factory = ZoraCreator1155FactoryImpl(factoryAddress);

vm.prank(factory.owner());

factory.upgradeTo(deployment.factoryImpl);

// sanity check - check minters match config
assertEq(address(factory.merkleMinter()), deployment.merkleMintSaleStrategy);
assertEq(address(factory.fixedPriceMinter()), deployment.fixedPriceSaleStrategy);
assertEq(address(factory.redeemMinterFactory()), deployment.redeemMinterFactory);

mintTokenAtFork(factory);
}
}
147 changes: 136 additions & 11 deletions packages/1155-contracts/test/nft/ZoraCreator1155.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,17 @@ import {SimpleRenderer} from "../mock/SimpleRenderer.sol";
contract MockTransferHookReceiver is ITransferHookReceiver {
mapping(uint256 => bool) public hasTransfer;

function onTokenTransferBatch(
address target,
address operator,
address from,
address to,
uint256[] memory ids,
uint256[] memory amounts,
bytes memory data
) external {
function onTokenTransferBatch(address, address, address, address, uint256[] memory ids, uint256[] memory, bytes memory) external {
for (uint256 i = 0; i < ids.length; i++) {
hasTransfer[ids[i]] = true;
}
}

function onTokenTransfer(address target, address operator, address from, address to, uint256 id, uint256 amount, bytes memory data) external {
function onTokenTransfer(address, address, address, address, uint256 id, uint256, bytes memory) external {
hasTransfer[id] = true;
}

function supportsInterface(bytes4 testInterface) external view override returns (bool) {
function supportsInterface(bytes4 testInterface) external pure override returns (bool) {
return testInterface == type(ITransferHookReceiver).interfaceId;
}
}
Expand Down Expand Up @@ -77,6 +69,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 +1140,126 @@ 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));

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));

uint256 creatorRewardBalance = settings.creatorReward + settings.firstMinterReward;
assertEq(protocolRewards.balanceOf(address(target)), creatorRewardBalance);

vm.prank(admin);
target.withdrawRewards(admin, creatorRewardBalance);

assertEq(admin.balance, creatorRewardBalance);
assertEq(protocolRewards.balanceOf(address(target)), 0);
}

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

0 comments on commit a70a355

Please sign in to comment.