Skip to content

Commit d89f7d2

Browse files
authored
Update EIP-5725: Move to Last Call
Merged by EIP-Bot.
1 parent 4982ae8 commit d89f7d2

10 files changed

Lines changed: 550 additions & 246 deletions

File tree

EIPS/eip-5725.md

Lines changed: 108 additions & 62 deletions
Large diffs are not rendered by default.

assets/eip-5725/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# EIP-5725: Transferrable Vesting NFT - Reference Implementation
2+
23
This repository serves as a reference implementation for **EIP-5725 Transferrable Vesting NFT Standard**. A Non-Fungible Token (NFT) standard used to vest ERC-20 tokens over a vesting release curve.
34

45
## Contents
6+
57
- [EIP-5725 Specification](./contracts/IERC5725.sol): Interface and definitions for the EIP-5725 specification.
6-
- [ERC-5725 Implementation (abstract)](./contracts/ERC5725.sol): ERC-5725 contract which can be extended to implement the specification.
8+
- [ERC-5725 Implementation (abstract)](./contracts/ERC5725.sol): ERC-5725 contract which can be extended to implement the specification.
79
- [VestingNFT Implementation](./contracts/reference/LinearVestingNFT.sol): Full ERC-5725 implementation using cliff vesting curve.
8-
- [LinearVestingNFT Implementation](./contracts/reference/VestingNFT.sol): Full ERC-5725 implementation using linear vesting curve.
10+
- [LinearVestingNFT Implementation](./contracts/reference/VestingNFT.sol): Full ERC-5725 implementation using linear vesting curve.

assets/eip-5725/contracts/ERC5725.sol

Lines changed: 117 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,13 @@ abstract contract ERC5725 is IERC5725, ERC721 {
1212
using SafeERC20 for IERC20;
1313

1414
/// @dev mapping for claimed payouts
15-
mapping(uint256 => uint256) /*tokenId*/ /*claimed*/
16-
internal _payoutClaimed;
15+
mapping(uint256 => uint256) /*tokenId*/ /*claimed*/ internal _payoutClaimed;
16+
17+
/// @dev Mapping from token ID to approved tokenId operator
18+
mapping(uint256 => address) private _tokenIdApprovals;
19+
20+
/// @dev Mapping from owner to operator approvals
21+
mapping(address => mapping(address => bool)) /* owner */ /*(operator, isApproved)*/ internal _operatorApprovals;
1722

1823
/**
1924
* @notice Checks if the tokenId exists and its valid
@@ -28,7 +33,8 @@ abstract contract ERC5725 is IERC5725, ERC721 {
2833
* @dev See {IERC5725}.
2934
*/
3035
function claim(uint256 tokenId) external override(IERC5725) validToken(tokenId) {
31-
require(ownerOf(tokenId) == msg.sender, "Not owner of NFT");
36+
require(isApprovedClaimOrOwner(msg.sender, tokenId), "ERC5725: not owner or operator");
37+
3238
uint256 amountClaimed = claimablePayout(tokenId);
3339
require(amountClaimed > 0, "ERC5725: No pending payout");
3440

@@ -38,6 +44,26 @@ abstract contract ERC5725 is IERC5725, ERC721 {
3844
IERC20(payoutToken(tokenId)).safeTransfer(msg.sender, amountClaimed);
3945
}
4046

47+
/**
48+
* @dev See {IERC5725}.
49+
*/
50+
function setClaimApprovalForAll(address operator, bool approved) external override(IERC5725) {
51+
_setClaimApprovalForAll(operator, approved);
52+
emit ClaimApprovalForAll(msg.sender, operator, approved);
53+
}
54+
55+
/**
56+
* @dev See {IERC5725}.
57+
*/
58+
function setClaimApproval(
59+
address operator,
60+
bool approved,
61+
uint256 tokenId
62+
) external override(IERC5725) validToken(tokenId) {
63+
_setClaimApproval(operator, tokenId);
64+
emit ClaimApproval(msg.sender, operator, tokenId, approved);
65+
}
66+
4167
/**
4268
* @dev See {IERC5725}.
4369
*/
@@ -48,62 +74,44 @@ abstract contract ERC5725 is IERC5725, ERC721 {
4874
/**
4975
* @dev See {IERC5725}.
5076
*/
51-
function vestedPayoutAtTime(uint256 tokenId, uint256 timestamp)
52-
public
53-
view
54-
virtual
55-
override(IERC5725)
56-
returns (uint256 payout);
77+
function vestedPayoutAtTime(
78+
uint256 tokenId,
79+
uint256 timestamp
80+
) public view virtual override(IERC5725) returns (uint256 payout);
5781

5882
/**
5983
* @dev See {IERC5725}.
6084
*/
61-
function vestingPayout(uint256 tokenId)
62-
public
63-
view
64-
override(IERC5725)
65-
validToken(tokenId)
66-
returns (uint256 payout)
67-
{
85+
function vestingPayout(
86+
uint256 tokenId
87+
) public view override(IERC5725) validToken(tokenId) returns (uint256 payout) {
6888
return _payout(tokenId) - vestedPayout(tokenId);
6989
}
7090

7191
/**
7292
* @dev See {IERC5725}.
7393
*/
74-
function claimablePayout(uint256 tokenId)
75-
public
76-
view
77-
override(IERC5725)
78-
validToken(tokenId)
79-
returns (uint256 payout)
80-
{
94+
function claimablePayout(
95+
uint256 tokenId
96+
) public view override(IERC5725) validToken(tokenId) returns (uint256 payout) {
8197
return vestedPayout(tokenId) - _payoutClaimed[tokenId];
8298
}
8399

84100
/**
85101
* @dev See {IERC5725}.
86102
*/
87-
function claimedPayout(uint256 tokenId)
88-
public
89-
view
90-
override(IERC5725)
91-
validToken(tokenId)
92-
returns (uint256 payout)
93-
{
103+
function claimedPayout(
104+
uint256 tokenId
105+
) public view override(IERC5725) validToken(tokenId) returns (uint256 payout) {
94106
return _payoutClaimed[tokenId];
95107
}
96108

97109
/**
98110
* @dev See {IERC5725}.
99111
*/
100-
function vestingPeriod(uint256 tokenId)
101-
public
102-
view
103-
override(IERC5725)
104-
validToken(tokenId)
105-
returns (uint256 vestingStart, uint256 vestingEnd)
106-
{
112+
function vestingPeriod(
113+
uint256 tokenId
114+
) public view override(IERC5725) validToken(tokenId) returns (uint256 vestingStart, uint256 vestingEnd) {
107115
return (_startTime(tokenId), _endTime(tokenId));
108116
}
109117

@@ -116,18 +124,81 @@ abstract contract ERC5725 is IERC5725, ERC721 {
116124

117125
/**
118126
* @dev See {IERC165-supportsInterface}.
119-
* IERC5725 interfaceId = 0x7c89676d
120-
*/
121-
function supportsInterface(bytes4 interfaceId)
122-
public
123-
view
124-
virtual
125-
override(ERC721, IERC165)
126-
returns (bool supported)
127-
{
127+
* IERC5725 interfaceId = 0xbd3a202b
128+
*/
129+
function supportsInterface(
130+
bytes4 interfaceId
131+
) public view virtual override(ERC721, IERC165) returns (bool supported) {
128132
return interfaceId == type(IERC5725).interfaceId || super.supportsInterface(interfaceId);
129133
}
130134

135+
/**
136+
* @dev See {IERC5725}.
137+
*/
138+
function getClaimApproved(uint256 tokenId) public view returns (address operator) {
139+
return _tokenIdApprovals[tokenId];
140+
}
141+
142+
/**
143+
* @dev Returns true if `owner` has set `operator` to manage all `tokenId`s.
144+
* @param owner The owner allowing `operator` to manage all `tokenId`s.
145+
* @param operator The address who is given permission to spend tokens on behalf of the `owner`.
146+
*/
147+
function isClaimApprovedForAll(address owner, address operator) public view returns (bool isClaimApproved) {
148+
return _operatorApprovals[owner][operator];
149+
}
150+
151+
/**
152+
* @dev Public view which returns true if the operator has permission to claim for `tokenId`
153+
* @notice To remove permissions, set operator to zero address.
154+
*
155+
* @param operator The address that has permission for a `tokenId`.
156+
* @param tokenId The NFT `tokenId`.
157+
*/
158+
function isApprovedClaimOrOwner(address operator, uint256 tokenId) public view virtual returns (bool) {
159+
address owner = ownerOf(tokenId);
160+
return (operator == owner || isClaimApprovedForAll(owner, operator) || getClaimApproved(tokenId) == operator);
161+
}
162+
163+
/**
164+
* @dev Internal function to set the operator status for a given owner to manage all `tokenId`s.
165+
* @notice To remove permissions, set approved to false.
166+
*
167+
* @param operator The address who is given permission to spend vested tokens.
168+
* @param approved The approved status.
169+
*/
170+
function _setClaimApprovalForAll(address operator, bool approved) internal virtual {
171+
_operatorApprovals[msg.sender][operator] = approved;
172+
}
173+
174+
/**
175+
* @dev Internal function to set the operator status for a given tokenId.
176+
* @notice To remove permissions, set operator to zero address.
177+
*
178+
* @param operator The address who is given permission to spend vested tokens.
179+
* @param tokenId The NFT `tokenId`.
180+
*/
181+
function _setClaimApproval(address operator, uint256 tokenId) internal virtual {
182+
require(ownerOf(tokenId) == msg.sender, "ERC5725: not owner of tokenId");
183+
_tokenIdApprovals[tokenId] = operator;
184+
}
185+
186+
/**
187+
* @dev Internal function to hook into {IERC721-_afterTokenTransfer}, when a token is being transferred.
188+
* Removes permissions to _tokenIdApprovals[tokenId] when the tokenId is transferred, burnt, but not on mint.
189+
*
190+
* @param from The address from which the tokens are being transferred.
191+
* @param to The address to which the tokens are being transferred.
192+
* @param firstTokenId The first tokenId in the batch that is being transferred.
193+
* @param batchSize The number of tokens being transferred in this batch.
194+
*/
195+
function _beforeTokenTransfer(address from, address to, uint256 firstTokenId, uint256 batchSize) internal override {
196+
super._beforeTokenTransfer(from, to, firstTokenId, batchSize);
197+
if (from != address(0)) {
198+
delete _tokenIdApprovals[firstTokenId];
199+
}
200+
}
201+
131202
/**
132203
* @dev Internal function to get the payout token of a given vesting NFT
133204
*

0 commit comments

Comments
 (0)