-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathNestedRecords.sol
208 lines (175 loc) · 8 KB
/
NestedRecords.sol
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.11;
import "./abstracts/OwnableFactoryHandler.sol";
/// @title Tracks data for underlying assets of NestedNFTs
contract NestedRecords is OwnableFactoryHandler {
/* ------------------------------ EVENTS ------------------------------ */
/// @dev Emitted when maxHoldingsCount is updated
/// @param maxHoldingsCount The new value
event MaxHoldingsChanges(uint256 maxHoldingsCount);
/// @dev Emitted when the lock timestamp of an NFT is increased
/// @param nftId The NFT ID
/// @param timestamp The new lock timestamp of the portfolio
event LockTimestampIncreased(uint256 nftId, uint256 timestamp);
/// @dev Emitted when the reserve is updated for a specific portfolio
/// @param nftId The NFT ID
/// @param newReserve The new reserve address
event reserveUpdated(uint256 nftId, address newReserve);
/* ------------------------------ STRUCTS ------------------------------ */
/// @dev Store user asset informations
struct NftRecord {
mapping(address => uint256) holdings;
address[] tokens;
address reserve;
uint256 lockTimestamp;
}
/* ----------------------------- VARIABLES ----------------------------- */
/// @dev stores for each NFT ID an asset record
mapping(uint256 => NftRecord) public records;
/// @dev The maximum number of holdings for an NFT record
uint256 public maxHoldingsCount;
/* ---------------------------- CONSTRUCTOR ---------------------------- */
constructor(uint256 _maxHoldingsCount) {
maxHoldingsCount = _maxHoldingsCount;
}
/* -------------------------- OWNER FUNCTIONS -------------------------- */
/// @notice Sets the maximum number of holdings for an NFT record
/// @param _maxHoldingsCount The new maximum number of holdings
function setMaxHoldingsCount(uint256 _maxHoldingsCount) external onlyOwner {
require(_maxHoldingsCount != 0, "NRC: INVALID_MAX_HOLDINGS");
maxHoldingsCount = _maxHoldingsCount;
emit MaxHoldingsChanges(maxHoldingsCount);
}
/* ------------------------- FACTORY FUNCTIONS ------------------------- */
/// @notice Update the amount for a specific holding and delete
/// the holding if the amount is zero.
/// @param _nftId The id of the NFT
/// @param _token The token/holding address
/// @param _amount Updated amount for this asset
function updateHoldingAmount(
uint256 _nftId,
address _token,
uint256 _amount
) public onlyFactory {
if (_amount == 0) {
uint256 tokenIndex = 0;
address[] memory tokens = getAssetTokens(_nftId);
while (tokenIndex < tokens.length) {
if (tokens[tokenIndex] == _token) {
deleteAsset(_nftId, tokenIndex);
break;
}
tokenIndex++;
}
} else {
records[_nftId].holdings[_token] = _amount;
}
}
/// @notice Fully delete a holding record for an NFT
/// @param _nftId The id of the NFT
/// @param _tokenIndex The token index in holdings array
function deleteAsset(uint256 _nftId, uint256 _tokenIndex) public onlyFactory {
address[] storage tokens = records[_nftId].tokens;
address token = tokens[_tokenIndex];
require(records[_nftId].holdings[token] != 0, "NRC: HOLDING_INACTIVE");
delete records[_nftId].holdings[token];
tokens[_tokenIndex] = tokens[tokens.length - 1];
tokens.pop();
}
/// @notice Delete a holding item in holding mapping. Does not remove token in NftRecord.tokens array
/// @param _nftId NFT's identifier
/// @param _token Token address for holding to remove
function freeHolding(uint256 _nftId, address _token) public onlyFactory {
delete records[_nftId].holdings[_token];
}
/// @notice Helper function that creates a record or add the holding if record already exists
/// @param _nftId The NFT's identifier
/// @param _token The token/holding address
/// @param _amount Amount to add for this asset
/// @param _reserve Reserve address
function store(
uint256 _nftId,
address _token,
uint256 _amount,
address _reserve
) external onlyFactory {
uint256 amount = records[_nftId].holdings[_token];
if (amount != 0) {
require(records[_nftId].reserve == _reserve, "NRC: RESERVE_MISMATCH");
updateHoldingAmount(_nftId, _token, amount + _amount);
return;
}
require(records[_nftId].tokens.length < maxHoldingsCount, "NRC: TOO_MANY_TOKENS");
require(
_reserve != address(0) && (_reserve == records[_nftId].reserve || records[_nftId].reserve == address(0)),
"NRC: INVALID_RESERVE"
);
records[_nftId].holdings[_token] = _amount;
records[_nftId].tokens.push(_token);
records[_nftId].reserve = _reserve;
}
/// @notice The factory can update the lock timestamp of a NFT record
/// The new timestamp must be greater than the records lockTimestamp
// if block.timestamp > actual lock timestamp
/// @param _nftId The NFT id to get the record
/// @param _timestamp The new timestamp
function updateLockTimestamp(uint256 _nftId, uint256 _timestamp) external onlyFactory {
require(_timestamp > records[_nftId].lockTimestamp, "NRC: LOCK_PERIOD_CANT_DECREASE");
records[_nftId].lockTimestamp = _timestamp;
emit LockTimestampIncreased(_nftId, _timestamp);
}
/// @notice Delete from mapping assetTokens
/// @param _nftId The id of the NFT
function removeNFT(uint256 _nftId) external onlyFactory {
delete records[_nftId];
}
/// @notice Set the reserve where assets are stored
/// @param _nftId The NFT ID to update
/// @param _nextReserve Address for the new reserve
function setReserve(uint256 _nftId, address _nextReserve) external onlyFactory {
records[_nftId].reserve = _nextReserve;
emit reserveUpdated(_nftId, _nextReserve);
}
/* ------------------------------- VIEWS ------------------------------- */
/// @notice Get content of assetTokens mapping
/// @param _nftId The id of the NFT>
function getAssetTokens(uint256 _nftId) public view returns (address[] memory) {
return records[_nftId].tokens;
}
/// @notice Get reserve the assets are stored in
/// @param _nftId The NFT ID
/// @return The reserve address these assets are stored in
function getAssetReserve(uint256 _nftId) external view returns (address) {
return records[_nftId].reserve;
}
/// @notice Get how many tokens are in a portfolio/NFT
/// @param _nftId NFT ID to examine
/// @return The array length
function getAssetTokensLength(uint256 _nftId) public view returns (uint256) {
return records[_nftId].tokens.length;
}
/// @notice Get holding object for this NFT ID
/// @param _nftId The id of the NFT
/// @param _token The address of the token
function getAssetHolding(uint256 _nftId, address _token) public view returns (uint256) {
return records[_nftId].holdings[_token];
}
/// @notice Returns the holdings associated to a NestedAsset
/// @param _nftId the id of the NestedAsset
/// @return The holdings
function tokenHoldings(uint256 _nftId) public view returns (address[] memory, uint256[] memory) {
address[] memory tokens = getAssetTokens(_nftId);
uint256 tokensCount = tokens.length;
uint256[] memory amounts = new uint256[](tokensCount);
for (uint256 i = 0; i < tokensCount; i++) {
amounts[i] = getAssetHolding(_nftId, tokens[i]);
}
return (tokens, amounts);
}
/// @notice Get the lock timestamp of a portfolio/NFT
/// @param _nftId The NFT ID
/// @return The lock timestamp from the NftRecord
function getLockTimestamp(uint256 _nftId) external view returns (uint256) {
return records[_nftId].lockTimestamp;
}
}