From c48a897b16c0680967dde8f9c3729eacf93e3a2e Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Mon, 1 Jul 2024 11:35:08 -0700 Subject: [PATCH 1/5] feat(spgnft): public minting, fee recipient, mint status, get/setters --- contracts/SPGNFT.sol | 92 +++++++++++++++++++++++++------- contracts/interfaces/ISPGNFT.sol | 56 ++++++++++++++----- contracts/lib/Errors.sol | 11 +++- 3 files changed, 127 insertions(+), 32 deletions(-) diff --git a/contracts/SPGNFT.sol b/contracts/SPGNFT.sol index c082a15..7560a76 100644 --- a/contracts/SPGNFT.sol +++ b/contracts/SPGNFT.sol @@ -16,12 +16,17 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { /// @param totalSupply The total minted supply of the collection. /// @param mintFee The fee to mint an NFT from the collection. /// @param mintFeeToken The token to pay for minting. + /// @param mintFeeRecipient The address to receive mint fees. + /// @param mintOpen The status of minting, whether it is open or not. /// @custom:storage-location erc7201:story-protocol-periphery.SPGNFT struct SPGNFTStorage { uint32 maxSupply; uint32 totalSupply; uint256 mintFee; address mintFeeToken; + address mintFeeRecipient; + bool mintOpen; + bool publicMinting; } // keccak256(abi.encode(uint256(keccak256("story-protocol-periphery.SPGNFT")) - 1)) & ~bytes32(uint256(0xff)); @@ -50,18 +55,24 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { /// @param maxSupply The maximum supply of the collection. /// @param mintFee The fee to mint an NFT from the collection. /// @param mintFeeToken The token to pay for minting. - /// @param owner The owner of the collection. + /// @param mintFeeRecipient The address to receive mint fees. + /// @param owner The owner of the collection. Zero address indicates no owner. + /// @param mintOpen Whether the collection is open for minting on creation. Configurable by the owner. + /// @param isPublicMinting If true, anyone can mint from the collection. If false, only the addresses with the + /// minter role can mint. Configurable by the owner. function initialize( string memory name, string memory symbol, uint32 maxSupply, uint256 mintFee, address mintFeeToken, - address owner + address mintFeeRecipient, + address owner, + bool mintOpen, + bool isPublicMinting ) public initializer { - if (owner == address(0) || (mintFee > 0 && mintFeeToken == address(0))) - revert Errors.SPGNFT__ZeroAddressParam(); - if (maxSupply == 0) revert Errors.SPGNFT_ZeroMaxSupply(); + if (mintFee > 0 && mintFeeToken == address(0)) revert Errors.SPGNFT__ZeroAddressParam(); + if (maxSupply == 0) revert Errors.SPGNFT__ZeroMaxSupply(); _grantRole(SPGNFTLib.ADMIN_ROLE, owner); _grantRole(SPGNFTLib.MINTER_ROLE, owner); @@ -76,6 +87,9 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { $.maxSupply = maxSupply; $.mintFee = mintFee; $.mintFeeToken = mintFeeToken; + $.mintFeeRecipient = mintFeeRecipient; + $.mintOpen = mintOpen; + $.publicMinting = isPublicMinting; __ERC721_init(name, symbol); } @@ -85,14 +99,36 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { return uint256(_getSPGNFTStorage().totalSupply); } + /// @notice Returns the current mint fee of the collection. + function mintFee() public view returns (uint256) { + return _getSPGNFTStorage().mintFee; + } + /// @notice Returns the current mint token of the collection. function mintFeeToken() public view returns (address) { return _getSPGNFTStorage().mintFeeToken; } - /// @notice Returns the current mint fee of the collection. - function mintFee() public view returns (uint256) { - return _getSPGNFTStorage().mintFee; + /// @notice Returns the current mint fee recipient of the collection. + function mintFeeRecipient() public view returns (address) { + return _getSPGNFTStorage().mintFeeRecipient; + } + + /// @notice Returns true if the collection is open for minting. + function mintOpen() public view returns (bool) { + return _getSPGNFTStorage().mintOpen; + } + + /// @notice Returns true if the collection is open for public minting. + function publicMinting() public view returns (bool) { + return _getSPGNFTStorage().publicMinting; + } + + /// @notice Sets the fee to mint an NFT from the collection. Payment is in the designated currency. + /// @dev Only callable by the admin role. + /// @param fee The new mint fee paid in the mint token. + function setMintFee(uint256 fee) public onlyRole(SPGNFTLib.ADMIN_ROLE) { + _getSPGNFTStorage().mintFee = fee; } /// @notice Sets the mint token for the collection. @@ -102,17 +138,37 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { _getSPGNFTStorage().mintFeeToken = token; } - /// @notice Sets the fee to mint an NFT from the collection. Payment is in the designated currency. + /// @notice Sets the recipient of mint fees. + /// @dev Only callable by the fee recipient. + /// @param newFeeRecipient The new fee recipient. + function setMintFeeRecipient(address newFeeRecipient) public { + if (msg.sender != _getSPGNFTStorage().mintFeeRecipient) { + revert Errors.SPGNFT__CallerNotFeeRecipient(); + } + _getSPGNFTStorage().mintFeeRecipient = newFeeRecipient; + } + + /// @notice Sets the minting status. /// @dev Only callable by the admin role. - /// @param fee The new mint fee paid in the mint token. - function setMintFee(uint256 fee) public onlyRole(SPGNFTLib.ADMIN_ROLE) { - _getSPGNFTStorage().mintFee = fee; + /// @param mintOpen Whether minting is open or not. + function setMintOpen(bool mintOpen) public onlyRole(SPGNFTLib.ADMIN_ROLE) { + _getSPGNFTStorage().mintOpen = mintOpen; + } + + /// @notice Sets the public minting status. + /// @dev Only callable by the admin role. + /// @param isPublicMinting Whether the collection is open for public minting or not. + function setPublicMinting(bool isPublicMinting) public onlyRole(SPGNFTLib.ADMIN_ROLE) { + _getSPGNFTStorage().publicMinting = isPublicMinting; } /// @notice Mints an NFT from the collection. Only callable by the minter role. /// @param to The address of the recipient of the minted NFT. /// @return tokenId The ID of the minted NFT. - function mint(address to) public onlyRole(SPGNFTLib.MINTER_ROLE) returns (uint256 tokenId) { + function mint(address to) public returns (uint256 tokenId) { + if (!_getSPGNFTStorage().publicMinting && !hasRole(SPGNFTLib.MINTER_ROLE, msg.sender)) { + revert Errors.SPGNFT__MintingDenied(); + } tokenId = _mintToken({ to: to, payer: msg.sender }); } @@ -124,11 +180,10 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { tokenId = _mintToken({ to: to, payer: payer }); } - /// @dev Withdraws the contract's token balance to the recipient. - /// @param recipient The token to withdraw. - /// @param recipient The address to receive the withdrawn balance. - function withdrawToken(address token, address recipient) public onlyRole(SPGNFTLib.ADMIN_ROLE) { - IERC20(token).transfer(recipient, IERC20(token).balanceOf(address(this))); + /// @dev Withdraws the contract's token balance to the fee recipient. + /// @param token The token to withdraw. + function withdrawToken(address token) public { + IERC20(token).transfer(_getSPGNFTStorage().mintFeeRecipient, IERC20(token).balanceOf(address(this))); } /// @dev Supports ERC165 interface. @@ -145,6 +200,7 @@ contract SPGNFT is ISPGNFT, ERC721Upgradeable, AccessControlUpgradeable { /// @return tokenId The ID of the minted NFT. function _mintToken(address to, address payer) internal returns (uint256 tokenId) { SPGNFTStorage storage $ = _getSPGNFTStorage(); + if (!$.mintOpen) revert Errors.SPGNFT__MintingClosed(); if ($.totalSupply + 1 > $.maxSupply) revert Errors.SPGNFT__MaxSupplyReached(); if ($.mintFeeToken != address(0) && $.mintFee > 0) { diff --git a/contracts/interfaces/ISPGNFT.sol b/contracts/interfaces/ISPGNFT.sol index b66fe7d..cd4c8bd 100644 --- a/contracts/interfaces/ISPGNFT.sol +++ b/contracts/interfaces/ISPGNFT.sol @@ -7,40 +7,71 @@ import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions interface ISPGNFT is IAccessControl, IERC721, IERC721Metadata { /// @dev Initializes the NFT collection. - /// @dev If mint cost is non-zero, mint token must be set. + /// @dev If mint fee is non-zero, mint token must be set. /// @param name The name of the collection. /// @param symbol The symbol of the collection. /// @param maxSupply The maximum supply of the collection. - /// @param mintFee The cost to mint an NFT from the collection. + /// @param mintFee The fee to mint an NFT from the collection. /// @param mintFeeToken The token to pay for minting. - /// @param owner The owner of the collection. + /// @param mintFeeRecipient The address to receive mint fees. + /// @param owner The owner of the collection. Zero address indicates no owner. + /// @param mintOpen Whether the collection is open for minting on creation. Configurable by the owner. + /// @param isPublicMinting If true, anyone can mint from the collection. If false, only the addresses with the + /// minter role can mint. Configurable by the owner. function initialize( string memory name, string memory symbol, uint32 maxSupply, uint256 mintFee, address mintFeeToken, - address owner + address mintFeeRecipient, + address owner, + bool mintOpen, + bool isPublicMinting ) external; /// @notice Returns the total minted supply of the collection. function totalSupply() external view returns (uint256); + /// @notice Returns the current mint fee of the collection. + function mintFee() external view returns (uint256); + /// @notice Returns the current mint token of the collection. function mintFeeToken() external view returns (address); - /// @notice Returns the current mint fee of the collection. - function mintFee() external view returns (uint256); + /// @notice Returns the current mint fee recipient of the collection. + function mintFeeRecipient() external view returns (address); + + /// @notice Returns true if the collection is open for minting. + function mintOpen() external view returns (bool); + + /// @notice Returns true if the collection is open for public minting. + function publicMinting() external view returns (bool); + + /// @notice Sets the fee to mint an NFT from the collection. Payment is in the designated currency. + /// @dev Only callable by the admin role. + /// @param fee The new mint fee paid in the mint token. + function setMintFee(uint256 fee) external; /// @notice Sets the mint token for the collection. /// @dev Only callable by the admin role. /// @param token The new mint token for mint payment. function setMintFeeToken(address token) external; - /// @notice Sets the fee to mint an NFT from the collection. Payment is in the designated currency. + /// @notice Sets the recipient of mint fees. + /// @dev Only callable by the fee recipient. + /// @param newFeeRecipient The new fee recipient. + function setMintFeeRecipient(address newFeeRecipient) external; + + /// @notice Sets the minting status. /// @dev Only callable by the admin role. - /// @param fee The new mint fee paid in the mint token. - function setMintFee(uint256 fee) external; + /// @param mintOpen Whether minting is open or not. + function setMintOpen(bool mintOpen) external; + + /// @notice Sets the public minting status. + /// @dev Only callable by the admin role. + /// @param isPublicMinting Whether the collection is open for public minting or not. + function setPublicMinting(bool isPublicMinting) external; /// @notice Mints an NFT from the collection. Only callable by the minter role. /// @param to The address of the recipient of the minted NFT. @@ -53,8 +84,7 @@ interface ISPGNFT is IAccessControl, IERC721, IERC721Metadata { /// @return tokenId The ID of the minted NFT. function mintBySPG(address to, address payer) external returns (uint256 tokenId); - /// @dev Withdraws the contract's token balance to the recipient. - /// @param recipient The token to withdraw. - /// @param recipient The address to receive the withdrawn balance. - function withdrawToken(address token, address recipient) external; + /// @dev Withdraws the contract's token balance to the fee recipient. + /// @param token The token to withdraw. + function withdrawToken(address token) external; } diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index cd3f1e2..ed383c2 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -17,11 +17,20 @@ library Errors { error SPGNFT__ZeroAddressParam(); /// @notice Zero max supply provided. - error SPGNFT_ZeroMaxSupply(); + error SPGNFT__ZeroMaxSupply(); /// @notice Max mint supply reached. error SPGNFT__MaxSupplyReached(); + /// @notice Minting is denied if the public minting is false (=> private) but caller does not have the minter role. + error SPGNFT__MintingDenied(); + /// @notice Caller is not the StoryProtocolGateway. error SPGNFT__CallerNotSPG(); + + /// @notice Caller is not the fee recipient. + error SPGNFT__CallerNotFeeRecipient(); + + /// @notice Minting is closed. + error SPGNFT__MintingClosed(); } From 71aec6f778bfd8d08ca93118701f919e706162e8 Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Mon, 1 Jul 2024 11:35:33 -0700 Subject: [PATCH 2/5] feat(spg): public minting, fee recipient, mint status --- contracts/StoryProtocolGateway.sol | 23 ++++++++++++++++--- .../interfaces/IStoryProtocolGateway.sol | 11 +++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/contracts/StoryProtocolGateway.sol b/contracts/StoryProtocolGateway.sol index 2cc2e8e..52a3468 100644 --- a/contracts/StoryProtocolGateway.sol +++ b/contracts/StoryProtocolGateway.sol @@ -116,7 +116,11 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable /// @param maxSupply The maximum supply of the collection. /// @param mintFee The cost to mint an NFT from the collection. /// @param mintFeeToken The token to be used for mint payment. - /// @param owner The owner of the collection. + /// @param mintFeeRecipient The address to receive mint fees. + /// @param owner The owner of the collection. Zero address indicates no owner. + /// @param mintOpen Whether the collection is open for minting on creation. Configurable by the owner. + /// @param isPublicMinting If true, anyone can mint from the collection. If false, only the addresses with the + /// minter role can mint. Configurable by the owner. /// @return nftContract The address of the newly created NFT collection. function createCollection( string calldata name, @@ -124,10 +128,23 @@ contract StoryProtocolGateway is IStoryProtocolGateway, AccessManagedUpgradeable uint32 maxSupply, uint256 mintFee, address mintFeeToken, - address owner + address mintFeeRecipient, + address owner, + bool mintOpen, + bool isPublicMinting ) external returns (address nftContract) { nftContract = address(new BeaconProxy(_getSPGStorage().nftContractBeacon, "")); - ISPGNFT(nftContract).initialize(name, symbol, maxSupply, mintFee, mintFeeToken, owner); + ISPGNFT(nftContract).initialize( + name, + symbol, + maxSupply, + mintFee, + mintFeeToken, + mintFeeRecipient, + owner, + mintOpen, + isPublicMinting + ); emit CollectionCreated(nftContract); } diff --git a/contracts/interfaces/IStoryProtocolGateway.sol b/contracts/interfaces/IStoryProtocolGateway.sol index 306d41f..5d7db2d 100644 --- a/contracts/interfaces/IStoryProtocolGateway.sol +++ b/contracts/interfaces/IStoryProtocolGateway.sol @@ -46,7 +46,11 @@ interface IStoryProtocolGateway { /// @param maxSupply The maximum supply of the collection. /// @param mintFee The cost to mint an NFT from the collection. /// @param mintFeeToken The token to be used for mint payment. - /// @param owner The owner of the collection. + /// @param mintFeeRecipient The address to receive mint fees. + /// @param owner The owner of the collection. Zero address indicates no owner. + /// @param mintOpen Whether the collection is open for minting on creation. Configurable by the owner. + /// @param isPublicMinting If true, anyone can mint from the collection. If false, only the addresses with the + /// minter role can mint. Configurable by the owner. /// @return nftContract The address of the newly created NFT collection. function createCollection( string calldata name, @@ -54,7 +58,10 @@ interface IStoryProtocolGateway { uint32 maxSupply, uint256 mintFee, address mintFeeToken, - address owner + address mintFeeRecipient, + address owner, + bool mintOpen, + bool isPublicMinting ) external returns (address nftContract); /// @notice Mint an NFT from a collection and register it with metadata as an IP. From 03cfb3e6276263cad86cbca66cdced18803f3ec5 Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Mon, 1 Jul 2024 11:36:01 -0700 Subject: [PATCH 3/5] fix(package): change versions and protocol pkgs --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 43799c6..d467891 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@story-protocol/protocol-periphery", - "version": "v1.0.0-rc1", + "version": "v1.0.0", "description": "Story Protocol periphery smart contracts", "main": "", "directories": { @@ -40,7 +40,8 @@ "@openzeppelin/contracts": "5.0.1", "@openzeppelin/contracts-upgradeable": "5.0.1", "@story-protocol/create3-deployer": "github:storyprotocol/create3-deployer#main", - "@story-protocol/protocol-core": "github:storyprotocol/protocol-core-v1#main", + "@story-protocol/protocol-core": "github:storyprotocol/protocol-core-v1#v1.0.0", + "erc6551": "^0.3.1", "solady": "^0.0.192" } } From bb95282944911967718beb8fa1481dda93423738 Mon Sep 17 00:00:00 2001 From: Jongwon Park Date: Mon, 1 Jul 2024 11:36:32 -0700 Subject: [PATCH 4/5] fix(test): update tests for modified SPG & SPGNFT, change base test to core v1.0.0 --- test/SPGNFT.t.sol | 61 +++++++++++++++++---------------- test/StoryProtocolGateway.t.sol | 15 +++++--- test/utils/BaseTest.t.sol | 20 +++++------ 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/test/SPGNFT.t.sol b/test/SPGNFT.t.sol index 159bbc8..bdac2b2 100644 --- a/test/SPGNFT.t.sol +++ b/test/SPGNFT.t.sol @@ -15,10 +15,13 @@ import { BaseTest } from "./utils/BaseTest.t.sol"; contract SPGNFTTest is BaseTest { ISPGNFT internal nftContract; + address internal feeRecipient; function setUp() public override { super.setUp(); + feeRecipient = address(0xbeef); + nftContract = ISPGNFT( spg.createCollection({ name: "Test Collection", @@ -26,7 +29,10 @@ contract SPGNFTTest is BaseTest { maxSupply: 100, mintFee: 100 * 10 ** mockToken.decimals(), mintFeeToken: address(mockToken), - owner: alice + mintFeeRecipient: alice, + owner: alice, + mintOpen: true, + isPublicMinting: false }) ); } @@ -42,7 +48,10 @@ contract SPGNFTTest is BaseTest { maxSupply: 100, mintFee: 100 * 10 ** mockToken.decimals(), mintFeeToken: address(mockToken), - owner: alice + mintFeeRecipient: feeRecipient, + owner: alice, + mintOpen: true, + isPublicMinting: false }); assertEq(nftContract.name(), anotherNftContract.name()); @@ -50,6 +59,10 @@ contract SPGNFTTest is BaseTest { assertEq(nftContract.totalSupply(), anotherNftContract.totalSupply()); assertTrue(anotherNftContract.hasRole(SPGNFTLib.MINTER_ROLE, alice)); assertEq(anotherNftContract.mintFee(), 100 * 10 ** mockToken.decimals()); + assertEq(anotherNftContract.mintFeeToken(), address(mockToken)); + assertEq(anotherNftContract.mintFeeRecipient(), feeRecipient); + assertTrue(anotherNftContract.mintOpen()); + assertFalse(anotherNftContract.publicMinting()); } function test_SPGNFT_initialize_revert_zeroParams() public { @@ -57,16 +70,6 @@ contract SPGNFTTest is BaseTest { address NFT_CONTRACT_BEACON = address(new UpgradeableBeacon(spgNftImpl, deployer)); nftContract = ISPGNFT(address(new BeaconProxy(NFT_CONTRACT_BEACON, ""))); - vm.expectRevert(Errors.SPGNFT__ZeroAddressParam.selector); - nftContract.initialize({ - name: "Test Collection", - symbol: "TEST", - maxSupply: 100, - mintFee: 0, - mintFeeToken: address(mockToken), - owner: address(0) - }); - vm.expectRevert(Errors.SPGNFT__ZeroAddressParam.selector); nftContract.initialize({ name: "Test Collection", @@ -74,17 +77,23 @@ contract SPGNFTTest is BaseTest { maxSupply: 100, mintFee: 1, mintFeeToken: address(0), - owner: alice + mintFeeRecipient: feeRecipient, + owner: alice, + mintOpen: true, + isPublicMinting: false }); - vm.expectRevert(Errors.SPGNFT_ZeroMaxSupply.selector); + vm.expectRevert(Errors.SPGNFT__ZeroMaxSupply.selector); nftContract.initialize({ name: "Test Collection", symbol: "TEST", maxSupply: 0, mintFee: 0, mintFeeToken: address(mockToken), - owner: alice + mintFeeRecipient: feeRecipient, + owner: alice, + mintOpen: true, + isPublicMinting: false }); } @@ -194,6 +203,9 @@ contract SPGNFTTest is BaseTest { } function test_SPGNFT_withdrawToken() public { + vm.prank(alice); + nftContract.setMintFeeRecipient(feeRecipient); + vm.startPrank(alice); mockToken.mint(address(alice), 1000 * 10 ** mockToken.decimals()); @@ -201,25 +213,14 @@ contract SPGNFTTest is BaseTest { uint256 mintFee = nftContract.mintFee(); - nftContract.mint(bob); + nftContract.mint(feeRecipient); assertEq(mockToken.balanceOf(address(nftContract)), mintFee); - uint256 balanceBeforeBob = mockToken.balanceOf(bob); + uint256 balanceBeforeFeeRecipient = mockToken.balanceOf(feeRecipient); - nftContract.withdrawToken(address(mockToken), bob); + nftContract.withdrawToken(address(mockToken)); assertEq(mockToken.balanceOf(address(nftContract)), 0); - assertEq(mockToken.balanceOf(bob), balanceBeforeBob + mintFee); - - vm.stopPrank(); - } - - function test_SPGNFT_revert_withdrawETH_accessControlUnauthorizedAccount() public { - vm.startPrank(bob); - - vm.expectRevert( - abi.encodeWithSelector(IAccessControl.AccessControlUnauthorizedAccount.selector, bob, SPGNFTLib.ADMIN_ROLE) - ); - nftContract.withdrawToken(address(mockToken), bob); + assertEq(mockToken.balanceOf(feeRecipient), balanceBeforeFeeRecipient + mintFee); vm.stopPrank(); } diff --git a/test/StoryProtocolGateway.t.sol b/test/StoryProtocolGateway.t.sol index 9c14861..bbeaf02 100644 --- a/test/StoryProtocolGateway.t.sol +++ b/test/StoryProtocolGateway.t.sol @@ -26,6 +26,7 @@ contract StoryProtocolGatewayTest is BaseTest { ISPGNFT internal nftContract; address internal minter; + address internal feeRecipient; address internal caller; mapping(uint256 index => IPAsset) internal ipAsset; address internal ipIdParent; @@ -36,6 +37,7 @@ contract StoryProtocolGatewayTest is BaseTest { function setUp() public override { super.setUp(); minter = alice; + feeRecipient = bob; metadataEmpty = ISPG.IPMetadata({ metadataURI: "", metadataHash: "", nftMetadataHash: "" }); metadataDefault = ISPG.IPMetadata({ @@ -53,20 +55,25 @@ contract StoryProtocolGatewayTest is BaseTest { maxSupply: 100, mintFee: 100 * 10 ** mockToken.decimals(), mintFeeToken: address(mockToken), - owner: minter + mintFeeRecipient: feeRecipient, + owner: minter, + mintOpen: true, + isPublicMinting: false }) ); _; } function test_SPG_createCollection() public withCollection { - uint256 mintFee = nftContract.mintFee(); - assertEq(nftContract.name(), "Test Collection"); assertEq(nftContract.symbol(), "TEST"); assertEq(nftContract.totalSupply(), 0); assertTrue(nftContract.hasRole(SPGNFTLib.MINTER_ROLE, alice)); - assertEq(mintFee, 100 * 10 ** mockToken.decimals()); + assertEq(nftContract.mintFee(), 100 * 10 ** mockToken.decimals()); + assertEq(nftContract.mintFeeToken(), address(mockToken)); + assertEq(nftContract.mintFeeRecipient(), bob); + assertTrue(nftContract.mintOpen()); + assertFalse(nftContract.publicMinting()); } modifier whenCallerDoesNotHaveMinterRole() { diff --git a/test/utils/BaseTest.t.sol b/test/utils/BaseTest.t.sol index 0d1c40c..e16794a 100644 --- a/test/utils/BaseTest.t.sol +++ b/test/utils/BaseTest.t.sol @@ -119,7 +119,7 @@ contract BaseTest is Test { address ipAccountRegistry = address(ipAssetRegistry); - impl = address(new AccessController(address(ipAssetRegistry), address(moduleRegistry))); + impl = address(new AccessController()); accessController = AccessController( TestProxyHelper.deployUUPSProxy( create3Deployer, @@ -134,12 +134,7 @@ contract BaseTest is Test { ); require(_loadProxyImpl(address(accessController)) == impl, "AccessController Proxy Implementation Mismatch"); - impl = address( - new LicenseRegistry( - _getDeployedAddress(type(LicensingModule).name), - _getDeployedAddress(type(DisputeModule).name) - ) - ); + impl = address(new LicenseRegistry()); licenseRegistry = LicenseRegistry( TestProxyHelper.deployUUPSProxy( create3Deployer, @@ -190,7 +185,6 @@ contract BaseTest is Test { impl = address( new RoyaltyModule( - _getDeployedAddress(type(LicensingModule).name), address(disputeModule), address(licenseRegistry) ) @@ -213,7 +207,6 @@ contract BaseTest is Test { new LicensingModule( address(accessController), address(ipAccountRegistry), - address(moduleRegistry), address(royaltyModule), address(licenseRegistry), address(disputeModule), @@ -234,7 +227,7 @@ contract BaseTest is Test { ); require(_loadProxyImpl(address(licensingModule)) == impl, "LicensingModule Proxy Implementation Mismatch"); - impl = address(new LicenseToken(address(licensingModule), address(disputeModule))); + impl = address(new LicenseToken()); licenseToken = LicenseToken( TestProxyHelper.deployUUPSProxy( create3Deployer, @@ -310,6 +303,13 @@ contract BaseTest is Test { moduleRegistry.registerModule("CORE_METADATA_MODULE", address(coreMetadataModule)); moduleRegistry.registerModule("CORE_METADATA_VIEW_MODULE", address(coreMetadataViewModule)); + accessController.setAddresses(address(ipAssetRegistry), address(moduleRegistry)); + licenseRegistry.setDisputeModule(address(disputeModule)); + licenseRegistry.setLicensingModule(address(licensingModule)); + licenseToken.setLicensingModule(address(licensingModule)); + licenseToken.setDisputeModule(address(disputeModule)); + royaltyModule.setLicensingModule(address(licensingModule)); + coreMetadataViewModule.updateCoreMetadataModule(); licenseRegistry.registerLicenseTemplate(address(pilTemplate)); } From 32d1524bd8680acf21b8652e4a74b53ca258a203 Mon Sep 17 00:00:00 2001 From: Sebastian Liu Date: Mon, 2 Sep 2024 12:37:56 -0700 Subject: [PATCH 5/5] fix(test): modify tests to work w/ the latest protocol --- contracts/SPGNFT.sol | 5 +---- contracts/lib/Errors.sol | 2 +- test/SPGNFT.t.sol | 1 - test/StoryProtocolGateway.t.sol | 15 ++++++++++----- test/utils/BaseTest.t.sol | 11 +++-------- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/contracts/SPGNFT.sol b/contracts/SPGNFT.sol index 36581e9..d419e7f 100644 --- a/contracts/SPGNFT.sol +++ b/contracts/SPGNFT.sol @@ -174,10 +174,7 @@ contract SPGNFT is ISPGNFT, ERC721URIStorageUpgradeable, AccessControlUpgradeabl /// @param to The address of the recipient of the minted NFT. /// @param nftMetadataURI OPTIONAL. The URI of the desired metadata for the newly minted NFT. /// @return tokenId The ID of the minted NFT. - function mint( - address to, - string calldata nftMetadataURI - ) public virtual returns (uint256 tokenId) { + function mint(address to, string calldata nftMetadataURI) public virtual returns (uint256 tokenId) { if (!_getSPGNFTStorage().publicMinting && !hasRole(SPGNFTLib.MINTER_ROLE, msg.sender)) { revert Errors.SPGNFT__MintingDenied(); } diff --git a/contracts/lib/Errors.sol b/contracts/lib/Errors.sol index a0f3f02..5c58163 100644 --- a/contracts/lib/Errors.sol +++ b/contracts/lib/Errors.sol @@ -33,7 +33,7 @@ library Errors { /// @notice Minting is closed. error SPGNFT__MintingClosed(); - + /// @notice Caller is not one of the periphery contracts. error SPGNFT__CallerNotPeripheryContract(); diff --git a/test/SPGNFT.t.sol b/test/SPGNFT.t.sol index 1677aec..987e2fe 100644 --- a/test/SPGNFT.t.sol +++ b/test/SPGNFT.t.sol @@ -14,7 +14,6 @@ import { Errors } from "../contracts/lib/Errors.sol"; import { BaseTest } from "./utils/BaseTest.t.sol"; contract SPGNFTTest is BaseTest { - address internal feeRecipient; string internal nftMetadataEmpty; string internal nftMetadataDefault; diff --git a/test/StoryProtocolGateway.t.sol b/test/StoryProtocolGateway.t.sol index 6bde58c..14630f6 100644 --- a/test/StoryProtocolGateway.t.sol +++ b/test/StoryProtocolGateway.t.sol @@ -21,8 +21,6 @@ contract StoryProtocolGatewayTest is BaseTest { address owner; } - address internal feeRecipient; - mapping(uint256 index => IPAsset) internal ipAsset; address internal ipIdParent; @@ -433,14 +431,17 @@ contract StoryProtocolGatewayTest is BaseTest { nftContracts = new ISPGNFT[](10); bytes[] memory data = new bytes[](10); for (uint256 i = 0; i < 10; i++) { - data[i] = abi.encodeWithSignature( - "createCollection(string,string,uint32,uint256,address,address)", + data[i] = abi.encodeWithSelector( + spg.createCollection.selector, "Test Collection", "TEST", 100, 100 * 10 ** mockToken.decimals(), address(mockToken), - minter + feeRecipient, + minter, + true, + false ); } @@ -455,6 +456,10 @@ contract StoryProtocolGatewayTest is BaseTest { assertEq(nftContracts[i].totalSupply(), 0); assertTrue(nftContracts[i].hasRole(SPGNFTLib.MINTER_ROLE, alice)); assertEq(nftContracts[i].mintFee(), 100 * 10 ** mockToken.decimals()); + assertEq(nftContracts[i].mintFeeToken(), address(mockToken)); + assertEq(nftContracts[i].mintFeeRecipient(), bob); + assertTrue(nftContracts[i].mintOpen()); + assertFalse(nftContracts[i].publicMinting()); } } diff --git a/test/utils/BaseTest.t.sol b/test/utils/BaseTest.t.sol index b4effe3..f38e51d 100644 --- a/test/utils/BaseTest.t.sol +++ b/test/utils/BaseTest.t.sol @@ -198,7 +198,7 @@ contract BaseTest is Test { impl = address(0); // Make sure we don't deploy wrong impl address ipAccountRegistry = address(ipAssetRegistry); - impl = address(new AccessController()); + impl = address(new AccessController(address(ipAssetRegistry), address(moduleRegistry))); accessController = AccessController( TestProxyHelper.deployUUPSProxy( create3Deployer, @@ -278,6 +278,7 @@ contract BaseTest is Test { impl = address(0); // Make sure we don't deploy wrong impl impl = address( new RoyaltyModule( + _getDeployedAddress(type(LicensingModule).name), address(disputeModule), address(licenseRegistry), address(ipAssetRegistry) @@ -302,6 +303,7 @@ contract BaseTest is Test { new LicensingModule( address(accessController), address(ipAccountRegistry), + address(moduleRegistry), address(royaltyModule), address(licenseRegistry), address(disputeModule), @@ -480,13 +482,6 @@ contract BaseTest is Test { moduleRegistry.registerModule("CORE_METADATA_VIEW_MODULE", address(coreMetadataViewModule)); moduleRegistry.registerModule("GROUPING_MODULE", address(groupingModule)); - accessController.setAddresses(address(ipAssetRegistry), address(moduleRegistry)); - licenseRegistry.setDisputeModule(address(disputeModule)); - licenseRegistry.setLicensingModule(address(licensingModule)); - licenseToken.setLicensingModule(address(licensingModule)); - licenseToken.setDisputeModule(address(disputeModule)); - royaltyModule.setLicensingModule(address(licensingModule)); - coreMetadataViewModule.updateCoreMetadataModule(); licenseRegistry.registerLicenseTemplate(address(pilTemplate));