diff --git a/contracts/SoundEdition/ISoundEditionV1.sol b/contracts/SoundEdition/ISoundEditionV1.sol index 0d3bb64b..983d8647 100644 --- a/contracts/SoundEdition/ISoundEditionV1.sol +++ b/contracts/SoundEdition/ISoundEditionV1.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.15; import "chiru-labs/ERC721A-Upgradeable/interfaces/IERC721AUpgradeable.sol"; import "openzeppelin-upgradeable/access/OwnableUpgradeable.sol"; +import "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol"; import "../modules/Metadata/IMetadataModule.sol"; /* @@ -34,7 +35,7 @@ import "../modules/Metadata/IMetadataModule.sol"; /// @title ISoundEditionV1 /// @author Sound.xyz -interface ISoundEditionV1 is IERC721AUpgradeable { +interface ISoundEditionV1 is IERC721AUpgradeable, IERC2981Upgradeable { function initialize( address _owner, string memory _name, @@ -52,4 +53,10 @@ interface ISoundEditionV1 is IERC721AUpgradeable { /// @param _to Address to mint to /// @param _quantity Number of tokens to mint function mint(address _to, uint256 _quantity) external payable; + + function supportsInterface(bytes4 interfaceId) + external + view + override(IERC721AUpgradeable, IERC165Upgradeable) + returns (bool); } diff --git a/contracts/SoundEdition/SoundEditionV1.sol b/contracts/SoundEdition/SoundEditionV1.sol index 837d3ea7..09bad497 100644 --- a/contracts/SoundEdition/SoundEditionV1.sol +++ b/contracts/SoundEdition/SoundEditionV1.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.15; import "chiru-labs/ERC721A-Upgradeable/extensions/ERC721AQueryableUpgradeable.sol"; import "openzeppelin-upgradeable/access/OwnableUpgradeable.sol"; -import "openzeppelin-upgradeable/interfaces/IERC2981Upgradeable.sol"; +import "./ISoundEditionV1.sol"; import "../modules/Metadata/IMetadataModule.sol"; import "openzeppelin-upgradeable/access/AccessControlUpgradeable.sol"; @@ -36,12 +36,7 @@ import "openzeppelin-upgradeable/access/AccessControlUpgradeable.sol"; /// @title SoundEditionV1 /// @author Sound.xyz -contract SoundEditionV1 is - ERC721AQueryableUpgradeable, - IERC2981Upgradeable, - OwnableUpgradeable, - AccessControlUpgradeable -{ +contract SoundEditionV1 is ISoundEditionV1, ERC721AQueryableUpgradeable, OwnableUpgradeable, AccessControlUpgradeable { // ================================ // CONSTANTS // ================================ @@ -151,13 +146,11 @@ contract SoundEditionV1 is return bytes(baseURI_).length != 0 ? string.concat(baseURI_, _toString(tokenId)) : ""; } - /// @notice Informs other contracts which interfaces this contract supports - /// @param _interfaceId The interface id to check - /// @dev https://eips.ethereum.org/EIPS/eip-165 + /// @inheritdoc ISoundEditionV1 function supportsInterface(bytes4 _interfaceId) public view - override(ERC721AUpgradeable, IERC721AUpgradeable, AccessControlUpgradeable, IERC165Upgradeable) + override(ISoundEditionV1, ERC721AUpgradeable, IERC721AUpgradeable, AccessControlUpgradeable) returns (bool) { return @@ -171,19 +164,13 @@ contract SoundEditionV1 is function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view - override + override(IERC2981Upgradeable) returns (address fundingRecipient, uint256 royaltyAmount) { // todo } - /// @notice Mints `_quantity` tokens to addrress `_to` - /// Each token will be assigned a token ID that is consecutively increasing. - /// The caller must have the `MINTER_ROLE`, which can be granted via - /// {grantRole}. Multiple minters, such as different minter contracts, - /// can be authorized simultaneously. - /// @param _to Address to mint to - /// @param _quantity Number of tokens to mint + /// @inheritdoc ISoundEditionV1 function mint(address _to, uint256 _quantity) public payable onlyRole(MINTER_ROLE) { _mint(_to, _quantity); } diff --git a/contracts/modules/Minters/FixedPricePermissionedSaleMinter.sol b/contracts/modules/Minters/FixedPricePermissionedSaleMinter.sol index c27d5e41..fafdd3c3 100644 --- a/contracts/modules/Minters/FixedPricePermissionedSaleMinter.sol +++ b/contracts/modules/Minters/FixedPricePermissionedSaleMinter.sol @@ -2,12 +2,12 @@ pragma solidity ^0.8.15; -import "./EditionMinter.sol"; +import "./MintControllerBase.sol"; import "../../SoundEdition/ISoundEditionV1.sol"; import "solady/utils/ECDSA.sol"; /// @dev Minter class for sales approved with signatures. -contract FixedPricePermissionedSaleMinter is EditionMinter { +contract FixedPricePermissionedSaleMinter is MintControllerBase { using ECDSA for bytes32; error WrongEtherValue(); diff --git a/contracts/modules/Minters/FixedPricePublicSaleMinter.sol b/contracts/modules/Minters/FixedPricePublicSaleMinter.sol index bf755a76..ae57c050 100644 --- a/contracts/modules/Minters/FixedPricePublicSaleMinter.sol +++ b/contracts/modules/Minters/FixedPricePublicSaleMinter.sol @@ -2,11 +2,11 @@ pragma solidity ^0.8.15; -import "./EditionMinter.sol"; +import "./MintControllerBase.sol"; import "../../SoundEdition/ISoundEditionV1.sol"; /// @dev Minter class for sales at a fixed price within a time range. -contract FixedPricePublicSaleMinter is EditionMinter { +contract FixedPricePublicSaleMinter is MintControllerBase { error WrongEtherValue(); error SoldOut(); diff --git a/contracts/modules/Minters/EditionMinter.sol b/contracts/modules/Minters/MintControllerBase.sol similarity index 77% rename from contracts/modules/Minters/EditionMinter.sol rename to contracts/modules/Minters/MintControllerBase.sol index 9f822af3..df7172c4 100644 --- a/contracts/modules/Minters/EditionMinter.sol +++ b/contracts/modules/Minters/MintControllerBase.sol @@ -2,20 +2,8 @@ pragma solidity ^0.8.15; -/// @dev The `EditionMinter` class provides common bookkeeping functions -/// for managing edition mint data in deriving contracts. -/// -/// A controller can create, edit, update, delete the mint data for an edition. -/// It is up to the deriving contract to restrict editing, updating, deleting -/// of mint data. A typical use case is to only allow the controller of the edition. -/// -/// Deriving contracts may use the existence of a controller to prevent overriding -/// of mint data. -/// -/// An edition can only have one controller at any time for a single deriving contract. -/// -/// An edition may have multiple controllers across different deriving contracts. -abstract contract EditionMinter { +/// @dev The `MintControllerBase` class maintains a central storage record of mint controllers. +abstract contract MintControllerBase { /// @dev The caller must be the the controller of this edition to perform this action. error MintControllerUnauthorized(); diff --git a/tests/modules/Minters/EditionMinter.t.sol b/tests/modules/Minters/MintControllerBase.t.sol similarity index 87% rename from tests/modules/Minters/EditionMinter.t.sol rename to tests/modules/Minters/MintControllerBase.t.sol index 49bf90ed..bc4dbaaa 100644 --- a/tests/modules/Minters/EditionMinter.t.sol +++ b/tests/modules/Minters/MintControllerBase.t.sol @@ -3,9 +3,9 @@ pragma solidity ^0.8.15; import "../../TestConfig.sol"; import "../../../contracts/SoundEdition/SoundEditionV1.sol"; import "../../../contracts/SoundCreator/SoundCreatorV1.sol"; -import "../../../contracts/modules/Minters/FixedPricePublicSaleMinter.sol"; +import "../../../contracts/modules/Minters/MintControllerBase.sol"; -contract EditionMinterTests is TestConfig, EditionMinter { +contract MintControllerBaseTests is TestConfig, MintControllerBase { function createEditionMintController(address edition) external { _createEditionMintController(edition); } @@ -35,11 +35,11 @@ contract EditionMinterTests is TestConfig, EditionMinter { address controller1 = getRandomAccount(1); vm.prank(controller0); this.createEditionMintController(address(edition)); - vm.expectRevert(abi.encodeWithSelector(EditionMinter.MintControllerAlreadyExists.selector, controller0)); + vm.expectRevert(abi.encodeWithSelector(MintControllerBase.MintControllerAlreadyExists.selector, controller0)); vm.prank(controller0); this.createEditionMintController(address(edition)); vm.prank(controller1); - vm.expectRevert(abi.encodeWithSelector(EditionMinter.MintControllerAlreadyExists.selector, controller0)); + vm.expectRevert(abi.encodeWithSelector(MintControllerBase.MintControllerAlreadyExists.selector, controller0)); this.createEditionMintController(address(edition)); } @@ -81,7 +81,7 @@ contract EditionMinterTests is TestConfig, EditionMinter { this.createEditionMintController(address(edition)); vm.prank(controller1); - vm.expectRevert(EditionMinter.MintControllerUnauthorized.selector); + vm.expectRevert(MintControllerBase.MintControllerUnauthorized.selector); this.deleteEditionMintController(address(edition)); } @@ -93,7 +93,7 @@ contract EditionMinterTests is TestConfig, EditionMinter { this.createEditionMintController(address(edition0)); vm.prank(controller); - vm.expectRevert(EditionMinter.MintControllerNotFound.selector); + vm.expectRevert(MintControllerBase.MintControllerNotFound.selector); this.deleteEditionMintController(address(edition1)); } }