From 56419eb5e25f8e69596feb21c338c401c384ea1f Mon Sep 17 00:00:00 2001 From: Tomi_Ohl Date: Mon, 29 Apr 2024 22:16:44 +0200 Subject: [PATCH] Add signedAt to ConfigurableGuildRewardNFT signature --- contracts/ConfigurableGuildRewardNFT.sol | 23 ++- .../IConfigurableGuildRewardNFT.sol | 24 ++- docs/contracts/ConfigurableGuildRewardNFT.md | 23 ++- .../interfaces/IConfigurableGuildRewardNFT.md | 29 +++- test/ConfigurableGuildRewardNFT.spec.ts | 143 +++++++++++++----- 5 files changed, 195 insertions(+), 47 deletions(-) diff --git a/contracts/ConfigurableGuildRewardNFT.sol b/contracts/ConfigurableGuildRewardNFT.sol index 072989d..2a30a3e 100644 --- a/contracts/ConfigurableGuildRewardNFT.sol +++ b/contracts/ConfigurableGuildRewardNFT.sol @@ -22,6 +22,8 @@ contract ConfigurableGuildRewardNFT is using ECDSA for bytes32; using LibTransfer for address payable; + uint256 public constant SIGNATURE_VALIDITY = 1 hours; + address public factoryProxy; uint256 public mintableAmountPerUser; @@ -45,11 +47,19 @@ contract ConfigurableGuildRewardNFT is _transferOwnership(nftConfig.tokenOwner); } - function claim(uint256 amount, address receiver, uint256 userId, bytes calldata signature) external payable { + function claim( + uint256 amount, + address receiver, + uint256 userId, + uint256 signedAt, + bytes calldata signature + ) external payable { + if (signedAt < block.timestamp - SIGNATURE_VALIDITY) revert ExpiredSignature(); + uint256 mintableAmount = mintableAmountPerUser; if (amount > mintableAmount - balanceOf(receiver) || amount > mintableAmount - claimedTokens[userId]) revert AlreadyClaimed(); - if (!isValidSignature(amount, receiver, userId, signature)) revert IncorrectSignature(); + if (!isValidSignature(amount, signedAt, receiver, userId, signature)) revert IncorrectSignature(); (uint256 guildFee, address payable guildTreasury) = ITreasuryManager(factoryProxy).getFeeData(); @@ -80,9 +90,11 @@ contract ConfigurableGuildRewardNFT is } else revert IncorrectFee(msg.value, guildAmount + ownerAmount); } - function burn(uint256[] calldata tokenIds, uint256 userId, bytes calldata signature) external { + function burn(uint256[] calldata tokenIds, uint256 userId, uint256 signedAt, bytes calldata signature) external { + if (signedAt < block.timestamp - SIGNATURE_VALIDITY) revert ExpiredSignature(); + uint256 amount = tokenIds.length; - if (!isValidSignature(amount, msg.sender, userId, signature)) revert IncorrectSignature(); + if (!isValidSignature(amount, signedAt, msg.sender, userId, signature)) revert IncorrectSignature(); for (uint256 i; i < amount; ) { uint256 tokenId = tokenIds[i]; @@ -126,12 +138,13 @@ contract ConfigurableGuildRewardNFT is /// @notice Checks the validity of the signature for the given params. function isValidSignature( uint256 amount, + uint256 signedAt, address receiver, uint256 userId, bytes calldata signature ) internal view returns (bool) { if (signature.length != 65) revert IncorrectSignature(); - bytes32 message = keccak256(abi.encode(amount, receiver, userId, block.chainid, address(this))) + bytes32 message = keccak256(abi.encode(amount, signedAt, receiver, userId, block.chainid, address(this))) .toEthSignedMessageHash(); return message.recover(signature) == IGuildRewardNFTFactory(factoryProxy).validSigner(); } diff --git a/contracts/interfaces/IConfigurableGuildRewardNFT.sol b/contracts/interfaces/IConfigurableGuildRewardNFT.sol index b6da4c2..cf7e245 100644 --- a/contracts/interfaces/IConfigurableGuildRewardNFT.sol +++ b/contracts/interfaces/IConfigurableGuildRewardNFT.sol @@ -15,6 +15,11 @@ interface IConfigurableGuildRewardNFT { /// @return mintableAmountPerUser The amount of tokens. function mintableAmountPerUser() external view returns (uint256 mintableAmountPerUser); + /// @notice The time interval while a signature is valid. + /// @return validity The time interval in seconds. + // solhint-disable func-name-mixedcase + function SIGNATURE_VALIDITY() external pure returns (uint256 validity); + /// @notice Returns the number of tokens the user claimed. /// @dev Analogous to balanceOf(address), but works with Guild user ids. /// @param userId The id of the user on Guild. @@ -34,14 +39,22 @@ interface IConfigurableGuildRewardNFT { /// @param amount The amount of tokens to mint. Should be less or equal to mintableAmountPerUser. /// @param receiver The address that receives the token. /// @param userId The id of the user on Guild. - /// @param signature The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. - function claim(uint256 amount, address receiver, uint256 userId, bytes calldata signature) external payable; + /// @param signedAt The timestamp marking the time when the data were signed. + /// @param signature The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. + function claim( + uint256 amount, + address receiver, + uint256 userId, + uint256 signedAt, + bytes calldata signature + ) external payable; /// @notice Burns tokens from the sender. /// @param tokenIds The tokenIds to burn. All of them should belong to userId. /// @param userId The id of the user on Guild. - /// @param signature The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. - function burn(uint256[] calldata tokenIds, uint256 userId, bytes calldata signature) external; + /// @param signedAt The timestamp marking the time when the data were signed. + /// @param signature The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. + function burn(uint256[] calldata tokenIds, uint256 userId, uint256 signedAt, bytes calldata signature) external; /// @notice Sets the locked (i.e. soulboundness) status of all of the tokens in this NFT. /// @dev Only callable by the owner. @@ -73,6 +86,9 @@ interface IConfigurableGuildRewardNFT { /// @notice Error thrown when the token is already claimed. error AlreadyClaimed(); + /// @notice Error thrown when the signature is already expired. + error ExpiredSignature(); + /// @notice Error thrown when an incorrect amount of fee is attempted to be paid. /// @param paid The amount of funds received. /// @param requiredAmount The amount of fees required for claiming a single token. diff --git a/docs/contracts/ConfigurableGuildRewardNFT.md b/docs/contracts/ConfigurableGuildRewardNFT.md index c9d7d25..695bfea 100644 --- a/docs/contracts/ConfigurableGuildRewardNFT.md +++ b/docs/contracts/ConfigurableGuildRewardNFT.md @@ -4,6 +4,19 @@ An NFT distributed as a reward for Guild.xyz users. ## Variables +### SIGNATURE_VALIDITY + +```solidity +uint256 SIGNATURE_VALIDITY +``` + +The time interval while a signature is valid. + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | + ### factoryProxy ```solidity @@ -79,6 +92,7 @@ function claim( uint256 amount, address receiver, uint256 userId, + uint256 signedAt, bytes signature ) external ``` @@ -92,7 +106,8 @@ Claims tokens to the given address. | `amount` | uint256 | The amount of tokens to mint. Should be less or equal to mintableAmountPerUser. | | `receiver` | address | The address that receives the token. | | `userId` | uint256 | The id of the user on Guild. | -| `signature` | bytes | The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. | +| `signedAt` | uint256 | The timestamp marking the time when the data were signed. | +| `signature` | bytes | The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. | ### burn @@ -100,6 +115,7 @@ Claims tokens to the given address. function burn( uint256[] tokenIds, uint256 userId, + uint256 signedAt, bytes signature ) external ``` @@ -112,7 +128,8 @@ Burns tokens from the sender. | :--- | :--- | :---------- | | `tokenIds` | uint256[] | The tokenIds to burn. All of them should belong to userId. | | `userId` | uint256 | The id of the user on Guild. | -| `signature` | bytes | The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. | +| `signedAt` | uint256 | The timestamp marking the time when the data were signed. | +| `signature` | bytes | The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. | ### setLocked @@ -212,6 +229,7 @@ See {IERC721Metadata-tokenURI}. ```solidity function isValidSignature( uint256 amount, + uint256 signedAt, address receiver, uint256 userId, bytes signature @@ -225,6 +243,7 @@ Checks the validity of the signature for the given params. | Name | Type | Description | | :--- | :--- | :---------- | | `amount` | uint256 | | +| `signedAt` | uint256 | | | `receiver` | address | | | `userId` | uint256 | | | `signature` | bytes | | diff --git a/docs/contracts/interfaces/IConfigurableGuildRewardNFT.md b/docs/contracts/interfaces/IConfigurableGuildRewardNFT.md index f89d021..7025c17 100644 --- a/docs/contracts/interfaces/IConfigurableGuildRewardNFT.md +++ b/docs/contracts/interfaces/IConfigurableGuildRewardNFT.md @@ -34,6 +34,19 @@ Doesn't matter if they are claimed in the same transaction or separately. | Name | Type | Description | | :--- | :--- | :---------- | | `mintableAmountPerUser` | uint256 | The amount of tokens. | +### SIGNATURE_VALIDITY + +```solidity +function SIGNATURE_VALIDITY() external returns (uint256 validity) +``` + +The time interval while a signature is valid. + +#### Return Values + +| Name | Type | Description | +| :--- | :--- | :---------- | +| `validity` | uint256 | The time interval in seconds. | ### balanceOf ```solidity @@ -84,6 +97,7 @@ function claim( uint256 amount, address receiver, uint256 userId, + uint256 signedAt, bytes signature ) external ``` @@ -97,7 +111,8 @@ Claims tokens to the given address. | `amount` | uint256 | The amount of tokens to mint. Should be less or equal to mintableAmountPerUser. | | `receiver` | address | The address that receives the token. | | `userId` | uint256 | The id of the user on Guild. | -| `signature` | bytes | The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. | +| `signedAt` | uint256 | The timestamp marking the time when the data were signed. | +| `signature` | bytes | The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. | ### burn @@ -105,6 +120,7 @@ Claims tokens to the given address. function burn( uint256[] tokenIds, uint256 userId, + uint256 signedAt, bytes signature ) external ``` @@ -117,7 +133,8 @@ Burns tokens from the sender. | :--- | :--- | :---------- | | `tokenIds` | uint256[] | The tokenIds to burn. All of them should belong to userId. | | `userId` | uint256 | The id of the user on Guild. | -| `signature` | bytes | The following signed by validSigner: amount, receiver, userId, chainId, the contract's address. | +| `signedAt` | uint256 | The timestamp marking the time when the data were signed. | +| `signature` | bytes | The following signed by validSigner: amount, signedAt, receiver, userId, chainId, the contract's address. | ### setLocked @@ -227,6 +244,14 @@ error AlreadyClaimed() Error thrown when the token is already claimed. +### ExpiredSignature + +```solidity +error ExpiredSignature() +``` + +Error thrown when the signature is already expired. + ### IncorrectFee ```solidity diff --git a/test/ConfigurableGuildRewardNFT.spec.ts b/test/ConfigurableGuildRewardNFT.spec.ts index 43be9d0..6c36d9c 100644 --- a/test/ConfigurableGuildRewardNFT.spec.ts +++ b/test/ConfigurableGuildRewardNFT.spec.ts @@ -33,6 +33,7 @@ let signer: SignerWithAddress; let chainId: BigNumberish; const sampleUserId = 42; +const sampleSignedAt = Math.floor(Date.now() / 1000); enum ContractType { BASIC_NFT, @@ -44,12 +45,13 @@ const createSignature = async ( amount: BigNumberish, receiver: string, userId: number, + signedAt: number, chainid: BigNumberish, nftAddress: string ) => { const payload = ethers.AbiCoder.defaultAbiCoder().encode( - ["uint256", "address", "uint256", "uint256", "address"], - [amount, receiver, userId, chainid, nftAddress] + ["uint256", "uint256", "address", "uint256", "uint256", "address"], + [amount, signedAt, receiver, userId, chainid, nftAddress] ); const payloadHash = ethers.keccak256(payload); return wallet.signMessage(ethers.getBytes(payloadHash)); @@ -105,29 +107,55 @@ describe("ConfigurableGuildRewardNFT", () => { sampleAmount, wallet0.address, sampleUserId, + sampleSignedAt, chainId, await nft.getAddress() ); }); context("#claim", () => { + it("should revert if the signature is expired", async () => { + const validity = await nft.SIGNATURE_VALIDITY(); + const oldTimestamp = Math.floor(Date.now() / 1000) - Number(validity) - 10; + await expect( + nft.claim( + sampleAmount, + wallet0.address, + sampleUserId, + oldTimestamp, + await createSignature( + signer, + sampleAmount, + wallet0.address, + sampleUserId, + oldTimestamp, + chainId, + await nft.getAddress() + ), + { + value: fee + nftConfig.tokenFee + } + ) + ).to.be.revertedWithCustomError(nft, "ExpiredSignature"); + }); + it("should revert if the address has already claimed all of their tokens", async () => { - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }) ).to.be.revertedWithCustomError(nft, "AlreadyClaimed"); }); it("should revert if the userId has already claimed all of their tokens", async () => { - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); await expect( - nft.claim(sampleAmount, randomWallet.address, sampleUserId, sampleSignature, { + nft.claim(sampleAmount, randomWallet.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }) ).to.be.revertedWithCustomError(nft, "AlreadyClaimed"); @@ -135,13 +163,13 @@ describe("ConfigurableGuildRewardNFT", () => { it("should revert if the signature is incorrect", async () => { await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, ethers.ZeroHash, { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, ethers.ZeroHash, { value: fee + nftConfig.tokenFee }) ).to.be.revertedWithCustomError(nft, "IncorrectSignature"); await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature.slice(0, -2), { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature.slice(0, -2), { value: fee + nftConfig.tokenFee }) ).to.be.revertedWithCustomError(nft, "IncorrectSignature"); @@ -151,11 +179,13 @@ describe("ConfigurableGuildRewardNFT", () => { sampleAmount, wallet0.address, sampleUserId, + sampleSignedAt, await createSignature( signer, sampleAmount, randomWallet.address, sampleUserId, + sampleSignedAt, chainId, await nft.getAddress() ), @@ -168,7 +198,7 @@ describe("ConfigurableGuildRewardNFT", () => { it("should increment the total supply", async () => { const totalSupply0 = await nft.totalSupply(); - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); const totalSupply1 = await nft.totalSupply(); @@ -177,7 +207,7 @@ describe("ConfigurableGuildRewardNFT", () => { it("should increment the address' claimed tokens", async () => { const userBalance0 = await nft["balanceOf(address)"](wallet0.address); - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); const userBalance1 = await nft["balanceOf(address)"](wallet0.address); @@ -186,7 +216,7 @@ describe("ConfigurableGuildRewardNFT", () => { it("should increment the userId's claimed tokens", async () => { const userBalance0 = await nft["balanceOf(uint256)"](sampleUserId); - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); const userBalance1 = await nft["balanceOf(uint256)"](sampleUserId); @@ -198,7 +228,7 @@ describe("ConfigurableGuildRewardNFT", () => { const tokenId = totalSupply; expect(await nft["balanceOf(address)"](wallet0.address)).to.eq(0); await expect(nft.ownerOf(tokenId)).to.be.revertedWith("ERC721: invalid token ID"); - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); expect(await nft["balanceOf(address)"](wallet0.address)).to.eq(1); @@ -212,6 +242,7 @@ describe("ConfigurableGuildRewardNFT", () => { amount, wallet0.address, sampleUserId, + sampleSignedAt, chainId, await nftMultipleMints.getAddress() ); @@ -222,7 +253,7 @@ describe("ConfigurableGuildRewardNFT", () => { const tokenId = totalSupply; expect(await nftMultipleMints["balanceOf(address)"](wallet0.address)).to.eq(0); await expect(nftMultipleMints.ownerOf(tokenId)).to.be.revertedWith("ERC721: invalid token ID"); - await nftMultipleMints.claim(amount, wallet0.address, sampleUserId, signature, { + await nftMultipleMints.claim(amount, wallet0.address, sampleUserId, sampleSignedAt, signature, { value: (fee + nftConfig.tokenFee) * amount }); expect(await nftMultipleMints["balanceOf(address)"](wallet0.address)).to.eq(3); @@ -232,7 +263,9 @@ describe("ConfigurableGuildRewardNFT", () => { it("should emit Locked event when minting soulbound tokens", async () => { const tokenId = await nft.totalSupply(); await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { value: fee + nftConfig.tokenFee }) + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { + value: fee + nftConfig.tokenFee + }) ) .to.emit(nft, "Locked") .withArgs(tokenId); @@ -240,7 +273,7 @@ describe("ConfigurableGuildRewardNFT", () => { it("should emit Claimed event", async () => { await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }) ) @@ -250,7 +283,7 @@ describe("ConfigurableGuildRewardNFT", () => { it("should transfer ether to both treasuries", async () => { await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }) ).to.changeEtherBalances( @@ -261,14 +294,14 @@ describe("ConfigurableGuildRewardNFT", () => { it("should revert if an incorrect msg.value is received", async () => { await expect( - nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee * 2n }) ) .to.be.revertedWithCustomError(nft, "IncorrectFee") .withArgs(fee * 2n, fee + nftConfig.tokenFee); - await expect(nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature)) + await expect(nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature)) .to.be.revertedWithCustomError(nft, "IncorrectFee") .withArgs(0, fee + nftConfig.tokenFee); }); @@ -276,41 +309,72 @@ describe("ConfigurableGuildRewardNFT", () => { context("#burn", () => { beforeEach("claim a token", async () => { - await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, sampleSignature, { value: fee + nftConfig.tokenFee }); }); + it("should revert if the signature is expired", async () => { + const validity = await nft.SIGNATURE_VALIDITY(); + const oldTimestamp = Math.floor(Date.now() / 1000) - Number(validity) - 10; + await expect( + nft.burn( + [0], + sampleUserId, + oldTimestamp, + await createSignature( + signer, + sampleAmount, + wallet0.address, + sampleUserId, + oldTimestamp, + chainId, + await nft.getAddress() + ) + ) + ).to.be.revertedWithCustomError(nft, "ExpiredSignature"); + }); + it("should revert if a token is attempted to be burned by anyone but it's owner", async () => { await expect( (nft.connect(randomWallet) as Contract).burn( [0], sampleUserId, - createSignature(signer, 1, randomWallet.address, sampleUserId, chainId, await nft.getAddress()) + sampleSignedAt, + createSignature( + signer, + 1, + randomWallet.address, + sampleUserId, + sampleSignedAt, + chainId, + await nft.getAddress() + ) ) ).to.be.revertedWithCustomError(nft, "IncorrectSender"); }); it("should revert if the signature is incorrect", async () => { - await expect(nft.burn([0], sampleUserId, ethers.ZeroHash)).to.be.revertedWithCustomError( + await expect(nft.burn([0], sampleUserId, sampleSignedAt, ethers.ZeroHash)).to.be.revertedWithCustomError( nft, "IncorrectSignature" ); - await expect(nft.burn([0], sampleUserId, sampleSignature.slice(0, -2))).to.be.revertedWithCustomError( - nft, - "IncorrectSignature" - ); + await expect( + nft.burn([0], sampleUserId, sampleSignedAt, sampleSignature.slice(0, -2)) + ).to.be.revertedWithCustomError(nft, "IncorrectSignature"); await expect( nft.burn( [0], sampleUserId, + sampleSignedAt, await createSignature( signer, sampleAmount, randomWallet.address, sampleUserId, + sampleSignedAt, chainId, await nft.getAddress() ) @@ -320,21 +384,21 @@ describe("ConfigurableGuildRewardNFT", () => { it("should decrement the address' claimed tokens", async () => { const userBalance0 = await nft["balanceOf(address)"](wallet0.address); - await nft.burn([0], sampleUserId, sampleSignature); + await nft.burn([0], sampleUserId, sampleSignedAt, sampleSignature); const userBalance1 = await nft["balanceOf(address)"](wallet0.address); expect(userBalance1).to.eq(userBalance0 - 1n); }); it("should decrement the userId's claimed tokens", async () => { const userBalance0 = await nft["balanceOf(uint256)"](sampleUserId); - await nft.burn([0], sampleUserId, sampleSignature); + await nft.burn([0], sampleUserId, sampleSignedAt, sampleSignature); const userBalance1 = await nft["balanceOf(uint256)"](sampleUserId); expect(userBalance1).to.eq(userBalance0 - 1n); }); it("should decrement the total supply", async () => { const totalSupply0 = await nft.totalSupply(); - await nft.burn([0], sampleUserId, sampleSignature); + await nft.burn([0], sampleUserId, sampleSignedAt, sampleSignature); const totalSupply1 = await nft.totalSupply(); expect(totalSupply1).to.eq(totalSupply0 - 1n); }); @@ -344,7 +408,7 @@ describe("ConfigurableGuildRewardNFT", () => { const tokenOfOwnerByIndex = await nft.tokenOfOwnerByIndex(wallet0.address, 0); expect(tokenOfOwnerByIndex).to.eq(tokenId); - await nft.burn([tokenId], sampleUserId, sampleSignature); + await nft.burn([tokenId], sampleUserId, sampleSignedAt, sampleSignature); await expect(nft.tokenOfOwnerByIndex(wallet0.address, 0)).to.be.revertedWith( "ERC721Enumerable: owner index out of bounds" @@ -357,13 +421,14 @@ describe("ConfigurableGuildRewardNFT", () => { 2n, wallet0.address, sampleUserId, + sampleSignedAt, chainId, await nftMultipleMints.getAddress() ); await factory.setNFTImplementation(ContractType.CONFIGURABLE_NFT, nftMultipleMints); - await nftMultipleMints.claim(2n, wallet0.address, sampleUserId, signature, { + await nftMultipleMints.claim(2n, wallet0.address, sampleUserId, sampleSignedAt, signature, { value: (fee + nftConfig.tokenFee) * 2n }); @@ -373,7 +438,7 @@ describe("ConfigurableGuildRewardNFT", () => { expect(tokenOfOwnerByIndex0).to.eq(tokenIds[0]); expect(tokenOfOwnerByIndex1).to.eq(tokenIds[1]); - await nftMultipleMints.burn(tokenIds, sampleUserId, signature); + await nftMultipleMints.burn(tokenIds, sampleUserId, sampleSignedAt, signature); await expect(nftMultipleMints.tokenOfOwnerByIndex(wallet0.address, 0)).to.be.revertedWith( "ERC721Enumerable: owner index out of bounds" @@ -392,7 +457,16 @@ describe("ConfigurableGuildRewardNFT", () => { sampleAmount, wallet0.address, sampleUserId, - await createSignature(signer, sampleAmount, wallet0.address, sampleUserId, chainId, await nft.getAddress()), + sampleSignedAt, + await createSignature( + signer, + sampleAmount, + wallet0.address, + sampleUserId, + sampleSignedAt, + chainId, + await nft.getAddress() + ), { value: fee + nftConfig.tokenFee } @@ -450,6 +524,7 @@ describe("ConfigurableGuildRewardNFT", () => { sampleAmount, wallet0.address, sampleUserId, + sampleSignedAt, chainId, await nft.getAddress() ); @@ -461,7 +536,7 @@ describe("ConfigurableGuildRewardNFT", () => { }); it("should return the correct tokenURI", async () => { - await nft.claim(sampleAmount, wallet0.address, sampleUserId, signature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, signature, { value: fee + nftConfig.tokenFee }); @@ -473,7 +548,7 @@ describe("ConfigurableGuildRewardNFT", () => { context("#updateTokenURI", () => { beforeEach("claim a token", async () => { - await nft.claim(sampleAmount, wallet0.address, sampleUserId, signature, { + await nft.claim(sampleAmount, wallet0.address, sampleUserId, sampleSignedAt, signature, { value: fee + nftConfig.tokenFee }); });