-
Notifications
You must be signed in to change notification settings - Fork 261
Expand file tree
/
Copy pathPoolMemberNFT.sol
More file actions
158 lines (127 loc) · 6.22 KB
/
PoolMemberNFT.sol
File metadata and controls
158 lines (127 loc) · 6.22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// SPDX-License-Identifier: AGPLv3
pragma solidity ^0.8.23;
import { IERC721Metadata } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
import { IPoolMemberNFT } from "../../interfaces/agreements/gdav1/IPoolMemberNFT.sol";
import { PoolNFTBase } from "./PoolNFTBase.sol";
import { IGeneralDistributionAgreementV1, ISuperfluid } from "../../interfaces/superfluid/ISuperfluid.sol";
import { ISuperfluidPool } from "../../interfaces/agreements/gdav1/ISuperfluidPool.sol";
import { ISuperfluidToken } from "../../interfaces/superfluid/ISuperfluidToken.sol";
/// DEPRECATED - the update hooks are no longer invoked.
contract PoolMemberNFT is PoolNFTBase, IPoolMemberNFT {
//// Storage Variables ////
/// NOTE: The storage variables in this contract MUST NOT:
/// - change the ordering of the existing variables
/// - change any of the variable types
/// - rename any of the existing variables
/// - remove any of the existing variables
/// @notice A mapping from token id to PoolMemberNFT data
/// PoolMemberNFTData: { address pool, address member, uint128 units }
/// @dev The token id is uint256(keccak256(abi.encode(pool, member)))
mapping(uint256 => PoolMemberNFTData) internal _poolMemberDataByTokenId;
constructor(ISuperfluid host, IGeneralDistributionAgreementV1 gdaV1) PoolNFTBase(host, gdaV1) { }
// note that this is used so we don't upgrade to wrong logic contract
function proxiableUUID() public pure override returns (bytes32) {
return keccak256("org.superfluid-finance.contracts.PoolMemberNFT.implementation");
}
function _ownerOf(uint256 tokenId) internal view override returns (address) {
return _poolMemberDataByTokenId[tokenId].member;
}
function poolMemberDataByTokenId(uint256 tokenId) public view override returns (PoolMemberNFTData memory data) {
return _poolMemberDataByTokenId[tokenId];
}
/// @notice Reverts - Transfer of pool member NFT is not allowed.
/// @dev We revert when users attempt to transfer pool member NFTs.
function _transfer(
address, // from,
address, // to,
uint256 // tokenId
) internal pure override {
revert POOL_NFT_TRANSFER_NOT_ALLOWED();
}
function getTokenId(address pool, address member) external view override returns (uint256 tokenId) {
return _getTokenId(pool, member);
}
function _getTokenId(address pool, address member) internal view returns (uint256 tokenId) {
return uint256(keccak256(abi.encode("PoolMemberNFT", block.chainid, pool, member)));
}
/// @inheritdoc PoolNFTBase
function tokenURI(uint256 tokenId) external view override(IERC721Metadata, PoolNFTBase) returns (string memory) {
return super._tokenURI(tokenId);
}
/// @notice Mints `newTokenId` and transfers it to `member`
/// @dev `pool` must be a registered pool in the GDA.
/// `newTokenId` must not exist, `member` cannot be `address(0)`, `pool` cannot be `address(0)`,
/// and `pool` cannot be `member`.
/// We emit a {Transfer} event.
/// @param pool The pool address
/// @param member The member address
function onCreate(address pool, address member) external override {
_mint(pool, member);
}
/// @notice Updates token with `tokenId`.
/// @dev `tokenId` must exist AND we emit a {MetadataUpdate} event
/// @param pool The pool address
/// @param member The member address
function onUpdate(address pool, address member) external override {
uint256 tokenId = _getTokenId(pool, member);
address owner = _ownerOf(tokenId);
assert(owner != address(0));
PoolMemberNFTData storage data = _poolMemberDataByTokenId[tokenId];
data.units = ISuperfluidPool(data.pool).getUnits(data.member);
_triggerMetadataUpdate(tokenId);
}
/// @notice Destroys token with `tokenId` and clears approvals from previous owner.
/// @dev `tokenId` must exist AND we emit a {Transfer} event
/// @param pool The pool address
/// @param member The member address
function onDelete(address pool, address member) external override {
uint256 tokenId = _getTokenId(pool, member);
_burn(tokenId);
}
function _mint(address pool, address member) internal {
ISuperfluidToken superToken = ISuperfluidPool(pool).superToken();
if (!GENERAL_DISTRIBUTION_AGREEMENT_V1.isPool(superToken, pool)) {
revert POOL_NFT_NOT_REGISTERED_POOL();
}
assert(pool != address(0));
assert(member != address(0));
assert(pool != member);
uint256 newTokenId = _getTokenId(pool, member);
assert(!_exists(newTokenId));
uint128 units = ISuperfluidPool(pool).getUnits(member);
if (units == 0) {
revert POOL_MEMBER_NFT_NO_UNITS();
}
// update mapping for new NFT to be minted
_poolMemberDataByTokenId[newTokenId] = PoolMemberNFTData(pool, member, units);
// emit mint of new pool member token with newTokenId
emit Transfer(address(0), member, newTokenId);
}
function _burn(uint256 tokenId) internal override {
PoolMemberNFTData storage data = _poolMemberDataByTokenId[tokenId];
if (ISuperfluidPool(data.pool).getUnits(data.member) > 0) {
revert POOL_MEMBER_NFT_HAS_UNITS();
}
address owner = _ownerOf(tokenId);
assert(owner != address(0));
super._burn(tokenId);
// remove previous tokenId flow data mapping
delete _poolMemberDataByTokenId[tokenId];
// emit burn of pool member token with tokenId
emit Transfer(owner, address(0), tokenId);
}
/// This was added after deprecating the PoolMemberNFT.
/// It allows owners of such tokens to get rid of them
/// in case it bothers them (e.g. cluttering the wallet).
function burn(uint256 tokenId) external {
address owner = _ownerOf(tokenId);
if (msg.sender != owner) {
revert POOL_MEMBER_NFT_ONLY_OWNER();
}
super._burn(tokenId);
// remove previous tokenId flow data mapping
delete _poolMemberDataByTokenId[tokenId];
// emit burn of pool member token with tokenId
emit Transfer(owner, address(0), tokenId);
}
}