Skip to content

Commit

Permalink
Add signedAt to ConfigurableGuildRewardNFT signature
Browse files Browse the repository at this point in the history
  • Loading branch information
TomiOhl committed Apr 29, 2024
1 parent 7166a8c commit 56419eb
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 47 deletions.
23 changes: 18 additions & 5 deletions contracts/ConfigurableGuildRewardNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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();

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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();
}
Expand Down
24 changes: 20 additions & 4 deletions contracts/interfaces/IConfigurableGuildRewardNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.

Check warning on line 43 in contracts/interfaces/IConfigurableGuildRewardNFT.sol

View workflow job for this annotation

GitHub Actions / lint

Line length must be no more than 123 but current length is 130
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.

Check warning on line 56 in contracts/interfaces/IConfigurableGuildRewardNFT.sol

View workflow job for this annotation

GitHub Actions / lint

Line length must be no more than 123 but current length is 130
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.
Expand Down Expand Up @@ -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.
Expand Down
23 changes: 21 additions & 2 deletions docs/contracts/ConfigurableGuildRewardNFT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -79,6 +92,7 @@ function claim(
uint256 amount,
address receiver,
uint256 userId,
uint256 signedAt,
bytes signature
) external
```
Expand All @@ -92,14 +106,16 @@ 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

```solidity
function burn(
uint256[] tokenIds,
uint256 userId,
uint256 signedAt,
bytes signature
) external
```
Expand All @@ -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

Expand Down Expand Up @@ -212,6 +229,7 @@ See {IERC721Metadata-tokenURI}.
```solidity
function isValidSignature(
uint256 amount,
uint256 signedAt,
address receiver,
uint256 userId,
bytes signature
Expand All @@ -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 | |
Expand Down
29 changes: 27 additions & 2 deletions docs/contracts/interfaces/IConfigurableGuildRewardNFT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -84,6 +97,7 @@ function claim(
uint256 amount,
address receiver,
uint256 userId,
uint256 signedAt,
bytes signature
) external
```
Expand All @@ -97,14 +111,16 @@ 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

```solidity
function burn(
uint256[] tokenIds,
uint256 userId,
uint256 signedAt,
bytes signature
) external
```
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand Down
Loading

0 comments on commit 56419eb

Please sign in to comment.