From 5d4157b923f8b9aa9afd2e7f2333a0f361d4bcf3 Mon Sep 17 00:00:00 2001 From: geronimo pylypchuk Date: Fri, 16 Feb 2024 17:01:26 -0300 Subject: [PATCH] feat: Invalidate nonce (#8) * feat: invalidate nonce * fix: typo * add require & test --- src/NftReward.sol | 10 ++++- test/NftReward.t.sol | 92 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) diff --git a/src/NftReward.sol b/src/NftReward.sol index c4c54cf..9176a92 100644 --- a/src/NftReward.sol +++ b/src/NftReward.sol @@ -211,9 +211,17 @@ contract NftReward is Initializable, ERC721Upgradeable, OwnableUpgradeable, Paus * @notice Upgrades contract to new implementation * @param newImplementation New implementation address */ - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} + /** + * @notice Invalidates nonce value to prevent mint request reusage + * @param _nonceValue Nonce value to invalidate + */ + function invalidateNonce(uint256 _nonceValue) external onlyOwner { + require(!nonceRedeemed[_nonceValue], "Already minted"); + nonceRedeemed[_nonceValue] = true; + } + //==================== // Internal methods //==================== diff --git a/test/NftReward.t.sol b/test/NftReward.t.sol index a3a798b..82b9042 100644 --- a/test/NftReward.t.sol +++ b/test/NftReward.t.sol @@ -527,4 +527,96 @@ contract NftRewardTest is Test { // after assertFalse(nftReward.paused()); } + + function testInvalidateNonce_ShouldInvalidateNonce() public { + // prepare arbitrary data keys + bytes32[] memory keys = new bytes32[](1); + keys[0] = keccak256("GITHUB_ORGANIZATION_NAME"); + // prepare arbitrary data values + string[] memory values = new string[](1); + values[0] = "ubiquity"; + // prepare mint request + NftReward.MintRequest memory mintRequest = NftReward.MintRequest({ + beneficiary: user1, + deadline: block.timestamp + 1, + keys: keys, + nonce: 1, + values: values + }); + // get mint request digest which should be signed + bytes32 digest = nftReward.getMintRequestDigest(mintRequest); + // minter signs mint request digest + (uint8 v, bytes32 r, bytes32 s) = vm.sign(minterPrivateKey, digest); + // get minter's signature + bytes memory signature = abi.encodePacked(r, s, v); + + uint tokenId = 0; + + // before + vm.expectRevert(); + nftReward.ownerOf(tokenId); + assertEq(nftReward.nonceRedeemed(1), false); + assertEq(nftReward.tokenDataKeyExists(keccak256("GITHUB_ORGANIZATION_NAME")), false); + + // owner invalidates + vm.prank(owner); + nftReward.invalidateNonce(1); + + // user try to mint + vm.prank(user1); + vm.expectRevert("Already minted"); + nftReward.safeMint(mintRequest, signature); + + // after + assertEq(nftReward.nonceRedeemed(1), true); + assertEq(nftReward.tokenIdCounter(), 0); + } + + function testRequireInvalidateNonce_ShouldRevert_IfNonceIsRedeemed() public { + // prepare arbitrary data keys + bytes32[] memory keys = new bytes32[](1); + keys[0] = keccak256("GITHUB_ORGANIZATION_NAME"); + // prepare arbitrary data values + string[] memory values = new string[](1); + values[0] = "ubiquity"; + // prepare mint request + NftReward.MintRequest memory mintRequest = NftReward.MintRequest({ + beneficiary: user1, + deadline: block.timestamp + 1, + keys: keys, + nonce: 1, + values: values + }); + // get mint request digest which should be signed + bytes32 digest = nftReward.getMintRequestDigest(mintRequest); + // minter signs mint request digest + (uint8 v, bytes32 r, bytes32 s) = vm.sign(minterPrivateKey, digest); + // get minter's signature + bytes memory signature = abi.encodePacked(r, s, v); + + uint tokenId = 0; + + // before + vm.expectRevert(); + nftReward.ownerOf(tokenId); + assertEq(nftReward.nonceRedeemed(1), false); + assertEq(nftReward.tokenDataKeyExists(keccak256("GITHUB_ORGANIZATION_NAME")), false); + + // user1 mints + vm.prank(user1); + nftReward.safeMint(mintRequest, signature); + + // owner try to invalidate + vm.prank(owner); + vm.expectRevert("Already minted"); + nftReward.invalidateNonce(1); + + // after + assertEq(nftReward.nonceRedeemed(1), true); + assertEq(nftReward.tokenDataKeys(0), keccak256("GITHUB_ORGANIZATION_NAME")); + assertEq(nftReward.tokenDataKeyExists(keccak256("GITHUB_ORGANIZATION_NAME")), true); + assertEq(nftReward.ownerOf(tokenId), user1); + assertEq(nftReward.tokenIdCounter(), 1); + assertEq(nftReward.tokenData(0, keccak256("GITHUB_ORGANIZATION_NAME")), "ubiquity"); + } }