From 1f2214b374e0466e50e9843e4fd974cdb0c036b8 Mon Sep 17 00:00:00 2001 From: neokry Date: Wed, 1 Nov 2023 12:15:06 +0700 Subject: [PATCH] Allow reserve until tokenId to be updated --- src/token/IToken.sol | 6 ++++ src/token/Token.sol | 17 +++++++++++ test/Token.t.sol | 73 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/src/token/IToken.sol b/src/token/IToken.sol index 8d24e3e..8f72ced 100644 --- a/src/token/IToken.sol +++ b/src/token/IToken.sol @@ -66,6 +66,12 @@ interface IToken is IUUPS, IERC721Votes, TokenTypesV1, TokenTypesV2 { /// @dev Reverts if the token is not reserved error TOKEN_NOT_RESERVED(); + /// @dev Reverts if the token reserve is being decreased + error CANNOT_DECREASE_RESERVE(); + + /// @dev Reverts if the token reserve cannot be changed + error CANNOT_CHANGE_RESERVE(); + /// /// /// FUNCTIONS /// /// /// diff --git a/src/token/Token.sol b/src/token/Token.sol index 3b40e3b..5886b03 100644 --- a/src/token/Token.sol +++ b/src/token/Token.sol @@ -481,6 +481,23 @@ contract Token is IToken, VersionedContract, UUPS, Ownable, ReentrancyGuard, ERC return minter[_minter]; } + function setReservedUntilTokenId(uint256 newReservedUntilTokenId) external onlyOwner { + // Cannot change the reserve after any non reserved tokens have been minted + // Added to prevent making any tokens inaccessible + if (settings.mintCount > 0) { + revert CANNOT_CHANGE_RESERVE(); + } + + // Cannot decrease the reserve if any tokens have been minted + // Added to prevent collisions with tokens being auctioned / vested + if (settings.totalSupply > 0 && reservedUntilTokenId > newReservedUntilTokenId) { + revert CANNOT_DECREASE_RESERVE(); + } + + // Set the new reserve + reservedUntilTokenId = newReservedUntilTokenId; + } + /// @notice Set a new metadata renderer /// @param newRenderer new renderer address to use function setMetadataRenderer(IBaseMetadata newRenderer) external { diff --git a/test/Token.t.sol b/test/Token.t.sol index 7f60484..a7680c2 100644 --- a/test/Token.t.sol +++ b/test/Token.t.sol @@ -931,4 +931,77 @@ contract TokenTest is NounsBuilderTest, TokenTypesV1 { token.ownerOf(i); } } + + function test_SetReservedUntilTokenId(uint256 _startingReserve, uint256 _newReserve) public { + deployAltMock(_startingReserve); + + vm.prank(founder); + token.setReservedUntilTokenId(_newReserve); + } + + function test_SetReservedUntilTokenIdAfterMint(uint256 _startingReserve, uint256 _newReserve) public { + vm.assume(_startingReserve > 0 && _newReserve > _startingReserve); + deployAltMock(_startingReserve); + + TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1); + TokenTypesV2.MinterParams memory p1 = TokenTypesV2.MinterParams({ minter: founder, allowed: true }); + minters[0] = p1; + + vm.prank(address(founder)); + token.updateMinters(minters); + + vm.prank(founder); + token.mintFromReserveTo(founder, 0); + + vm.prank(founder); + token.setReservedUntilTokenId(_newReserve); + } + + function testRevert_CannotSetReserveAfterDAOIsLaunched(uint256 _startingReserve, uint256 _newReserve) public { + deployAltMock(_startingReserve); + + vm.prank(founder); + auction.unpause(); + + vm.prank(token.owner()); + vm.expectRevert(abi.encodeWithSignature("CANNOT_CHANGE_RESERVE()")); + token.setReservedUntilTokenId(_newReserve); + } + + function testRevert_CannotSetReserveAfterVestedMint(uint256 _startingReserve, uint256 _newReserve) public { + deployAltMock(_startingReserve); + + TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1); + TokenTypesV2.MinterParams memory p1 = TokenTypesV2.MinterParams({ minter: founder, allowed: true }); + minters[0] = p1; + + vm.prank(address(founder)); + token.updateMinters(minters); + + vm.prank(founder); + token.mint(); + + vm.prank(token.owner()); + vm.expectRevert(abi.encodeWithSignature("CANNOT_CHANGE_RESERVE()")); + token.setReservedUntilTokenId(_newReserve); + } + + function testRevert_CannotReduceReserveAfterMint(uint256 _startingReserve, uint256 _newReserve) public { + vm.assume(_startingReserve > 0 && _startingReserve > _newReserve); + deployAltMock(_startingReserve); + + TokenTypesV2.MinterParams[] memory minters = new TokenTypesV2.MinterParams[](1); + TokenTypesV2.MinterParams memory p1 = TokenTypesV2.MinterParams({ minter: founder, allowed: true }); + minters[0] = p1; + + vm.prank(address(founder)); + token.updateMinters(minters); + + vm.prank(founder); + token.mintFromReserveTo(founder, 0); + + vm.prank(token.owner()); + vm.expectRevert(abi.encodeWithSignature("CANNOT_DECREASE_RESERVE()")); + token.setReservedUntilTokenId(_newReserve); + } }