-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathNFTCollectionFactory.sol
451 lines (412 loc) · 18.8 KB
/
NFTCollectionFactory.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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
/*
・
* ★
・ 。
・ ゚☆ 。
* ★ ゚・。 * 。
* ☆ 。・゚*.。
゚ *.。☆。★ ・
` .-:::::-.` `-::---...```
`-:` .:+ssssoooo++//:.` .-/+shhhhhhhhhhhhhyyyssooo:
.--::. .+ossso+/////++/:://-` .////+shhhhhhhhhhhhhhhhhhhhhy
`-----::. `/+////+++///+++/:--:/+/- -////+shhhhhhhhhhhhhhhhhhhhhy
`------:::-` `//-.``.-/+ooosso+:-.-/oso- -////+shhhhhhhhhhhhhhhhhhhhhy
.--------:::-` :+:.` .-/osyyyyyyso++syhyo.-////+shhhhhhhhhhhhhhhhhhhhhy
`-----------:::-. +o+:-.-:/oyhhhhhhdhhhhhdddy:-////+shhhhhhhhhhhhhhhhhhhhhy
.------------::::-- `oys+/::/+shhhhhhhdddddddddy/-////+shhhhhhhhhhhhhhhhhhhhhy
.--------------:::::-` +ys+////+yhhhhhhhddddddddhy:-////+yhhhhhhhhhhhhhhhhhhhhhy
`----------------::::::-`.ss+/:::+oyhhhhhhhhhhhhhhho`-////+shhhhhhhhhhhhhhhhhhhhhy
.------------------:::::::.-so//::/+osyyyhhhhhhhhhys` -////+shhhhhhhhhhhhhhhhhhhhhy
`.-------------------::/:::::..+o+////+oosssyyyyyyys+` .////+shhhhhhhhhhhhhhhhhhhhhy
.--------------------::/:::.` -+o++++++oooosssss/. `-//+shhhhhhhhhhhhhhhhhhhhyo
.------- ``````.......--` `-/+ooooosso+/-` `./++++///:::--...``hhhhyo
`````
*
・ 。
・ ゚☆ 。
* ★ ゚・。 * 。
* ☆ 。・゚*.。
゚ *.。☆。★ ・
* ゚。·*・。 ゚*
☆゚・。°*. ゚
・ ゚*。・゚★。
・ *゚。 *
・゚*。★・
☆∴。 *
・ 。
*/
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.12;
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "./interfaces/ICollectionFactory.sol";
import "./interfaces/INFTDropCollectionInitializer.sol";
import "./interfaces/INFTCollectionInitializer.sol";
import "./interfaces/IRoles.sol";
import "./libraries/AddressLibrary.sol";
import "./mixins/shared/Gap10000.sol";
/**
* @title A factory to create NFT collections.
* @notice Call this factory to create NFT collections.
* @dev This creates and initializes an ERC-1165 minimal proxy pointing to a NFT collection contract implementation.
*/
contract NFTCollectionFactory is ICollectionFactory, Initializable, Gap10000 {
using AddressUpgradeable for address;
using Clones for address;
using Strings for uint32;
/****** Slot 0 (after inheritance) ******/
/**
* @notice The address of the implementation all new NFTCollections will leverage.
* @dev When this is changed, `versionNFTCollection` is incremented.
* @return The implementation address for NFTCollection.
*/
address public implementationNFTCollection;
/**
* @notice The implementation version of new NFTCollections.
* @dev This is auto-incremented each time `implementationNFTCollection` is changed.
* @return The current NFTCollection implementation version.
*/
uint32 public versionNFTCollection;
/****** Slot 1 ******/
/**
* @notice The address of the implementation all new NFTDropCollections will leverage.
* @dev When this is changed, `versionNFTDropCollection` is incremented.
* @return The implementation address for NFTDropCollection.
*/
address public implementationNFTDropCollection;
/**
* @notice The implementation version of new NFTDropCollections.
* @dev This is auto-incremented each time `implementationNFTDropCollection` is changed.
* @return The current NFTDropCollection implementation version.
*/
uint32 public versionNFTDropCollection;
/****** End of storage ******/
/**
* @notice The contract address which manages common roles.
* @dev Defines a centralized admin role definition for permissioned functions below.
* @return The contract address with role definitions.
*/
IRoles public immutable rolesContract;
/**
* @notice Emitted when the implementation of NFTCollection used by new collections is updated.
* @param implementation The new implementation contract address.
* @param version The version of the new implementation, auto-incremented.
*/
event ImplementationNFTCollectionUpdated(address indexed implementation, uint256 indexed version);
/**
* @notice Emitted when the implementation of NFTDropCollection used by new collections is updated.
* @param implementationNFTDropCollection The new implementation contract address.
* @param version The version of the new implementation, auto-incremented.
*/
event ImplementationNFTDropCollectionUpdated(
address indexed implementationNFTDropCollection,
uint256 indexed version
);
/**
* @notice Emitted when a new NFTCollection is created from this factory.
* @param collection The address of the new NFT collection contract.
* @param creator The address of the creator which owns the new collection.
* @param version The implementation version used by the new collection.
* @param name The name of the collection contract created.
* @param symbol The symbol of the collection contract created.
* @param nonce The nonce used by the creator when creating the collection,
* used to define the address of the collection.
*/
event NFTCollectionCreated(
address indexed collection,
address indexed creator,
uint256 indexed version,
string name,
string symbol,
uint256 nonce
);
/**
* @notice Emitted when a new NFTDropCollection is created from this factory.
* @param collection The address of the new NFT drop collection contract.
* @param creator The address of the creator which owns the new collection.
* @param approvedMinter An optional address to grant the MINTER_ROLE.
* @param name The collection's `name`.
* @param symbol The collection's `symbol`.
* @param baseURI The base URI for the collection.
* @param postRevealBaseURIHash The hash of the revealed baseURI for the collection.
* Set to bytes32(0) if the content is revealed by default (note that revealed content is immutable).
* If the post reveal content is unknown, use bytes32(uint(1)) to indicate the `baseURI` is pre-reveal content.
* @param maxTokenId The max `tokenID` for this collection.
* @param paymentAddress The address that will receive royalties and mint payments.
* @param version The implementation version used by the new NFTDropCollection collection.
* @param nonce The nonce used by the creator to create this collection.
*/
event NFTDropCollectionCreated(
address indexed collection,
address indexed creator,
address indexed approvedMinter,
string name,
string symbol,
string baseURI,
bytes32 postRevealBaseURIHash,
uint256 maxTokenId,
address paymentAddress,
uint256 version,
uint256 nonce
);
modifier onlyAdmin() {
require(rolesContract.isAdmin(msg.sender), "NFTCollectionFactory: Caller does not have the Admin role");
_;
}
/**
* @notice Defines requirements for the collection drop factory at deployment time.
* @param _rolesContract The address of the contract defining roles for collections to use.
*/
constructor(address _rolesContract) {
require(_rolesContract.isContract(), "NFTCollectionFactory: RolesContract is not a contract");
rolesContract = IRoles(_rolesContract);
}
/**
* @notice Initializer called after contract creation.
* @dev This is used so that this factory will resume versions from where our original factory had left off.
* @param _versionNFTCollection The current implementation version for NFTCollections.
*/
function initialize(uint32 _versionNFTCollection) external initializer {
versionNFTCollection = _versionNFTCollection;
}
/**
* @notice Allows Foundation to change the NFTCollection implementation used for future collections.
* This call will auto-increment the version.
* Existing collections are not impacted.
* @param _implementation The new NFTCollection collection implementation address.
*/
function adminUpdateNFTCollectionImplementation(address _implementation) external onlyAdmin {
require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract");
implementationNFTCollection = _implementation;
unchecked {
// Version cannot overflow 256 bits.
versionNFTCollection++;
}
// The implementation is initialized when assigned so that others may not claim it as their own.
INFTCollectionInitializer(_implementation).initialize(
payable(address(rolesContract)),
string.concat("NFT Collection Implementation v", versionNFTCollection.toString()),
string.concat("NFTv", versionNFTCollection.toString())
);
emit ImplementationNFTCollectionUpdated(_implementation, versionNFTCollection);
}
/**
* @notice Allows Foundation to change the NFTDropCollection implementation used for future collections.
* This call will auto-increment the version.
* Existing collections are not impacted.
* @param _implementation The new NFTDropCollection collection implementation address.
*/
function adminUpdateNFTDropCollectionImplementation(address _implementation) external onlyAdmin {
require(_implementation.isContract(), "NFTCollectionFactory: Implementation is not a contract");
implementationNFTDropCollection = _implementation;
unchecked {
// Version cannot overflow 256 bits.
versionNFTDropCollection++;
}
emit ImplementationNFTDropCollectionUpdated(_implementation, versionNFTDropCollection);
// The implementation is initialized when assigned so that others may not claim it as their own.
INFTDropCollectionInitializer(_implementation).initialize(
payable(address(this)),
string.concat("NFT Drop Collection Implementation v", versionNFTDropCollection.toString()),
string.concat("NFTDropV", versionNFTDropCollection.toString()),
"ipfs://bafybeibvxnuaqtvaxu26gdgly2rm4g2piu7b2tqlx2dsz6wwhqbey2gddy/",
0x1337000000000000000000000000000000000000000000000000000000001337,
1,
address(0),
payable(0)
);
}
/**
* @notice Create a new collection contract.
* @dev The nonce must be unique for the msg.sender + implementation version, otherwise this call will revert.
* @param name The collection's `name`.
* @param symbol The collection's `symbol`.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @return collection The address of the newly created collection contract.
*/
function createNFTCollection(
string calldata name,
string calldata symbol,
uint256 nonce
) external returns (address collection) {
require(bytes(symbol).length != 0, "NFTCollectionFactory: Symbol is required");
// This reverts if the NFT was previously created using this implementation version + msg.sender + nonce
collection = implementationNFTCollection.cloneDeterministic(_getSalt(msg.sender, nonce));
INFTCollectionInitializer(collection).initialize(payable(msg.sender), name, symbol);
emit NFTCollectionCreated(collection, msg.sender, versionNFTCollection, name, symbol, nonce);
}
/**
* @notice Create a new drop collection contract.
* @dev The nonce must be unique for the msg.sender + implementation version, otherwise this call will revert.
* @param name The collection's `name`.
* @param symbol The collection's `symbol`.
* @param baseURI The base URI for the collection.
* @param postRevealBaseURIHash The hash of the revealed baseURI for the collection.
* Set to bytes32(0) if the content is revealed by default (note that revealed content is immutable).
* If the post reveal content is unknown, use bytes32(uint(1)) to indicate the `baseURI` is pre-reveal content.
* @param maxTokenId The max token id for this collection.
* @param approvedMinter An optional address to grant the MINTER_ROLE.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @return collection The address of the newly created collection contract.
*/
function createNFTDropCollection(
string calldata name,
string calldata symbol,
string calldata baseURI,
bytes32 postRevealBaseURIHash,
uint32 maxTokenId,
address approvedMinter,
uint256 nonce
) external returns (address collection) {
return
_createNFTDropCollection(
name,
symbol,
baseURI,
postRevealBaseURIHash,
maxTokenId,
approvedMinter,
payable(0),
nonce
);
}
/**
* @notice Create a new drop collection contract with a custom payment address.
* @dev All params other than `paymentAddress` are the same as in `createNFTDropCollection`.
* The nonce must be unique for the msg.sender + implementation version, otherwise this call will revert.
* @param name The collection's `name`.
* @param symbol The collection's `symbol`.
* @param baseURI The base URI for the collection.
* @param postRevealBaseURIHash The hash of the revealed baseURI for the collection.
* Set to bytes32(0) if the content is revealed by default (note that revealed content is immutable).
* If the post reveal content is unknown, use bytes32(uint(1)) to indicate the `baseURI` is pre-reveal content.
* @param maxTokenId The max token id for this collection.
* @param approvedMinter An optional address to grant the MINTER_ROLE.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @param paymentAddress The address that will receive royalties and mint payments.
* @return collection The address of the newly created collection contract.
*/
function createNFTDropCollectionWithPaymentAddress(
string calldata name,
string calldata symbol,
string calldata baseURI,
bytes32 postRevealBaseURIHash,
uint32 maxTokenId,
address approvedMinter,
uint256 nonce,
address payable paymentAddress
) external returns (address collection) {
return
_createNFTDropCollection(
name,
symbol,
baseURI,
postRevealBaseURIHash,
maxTokenId,
approvedMinter,
paymentAddress != msg.sender ? paymentAddress : payable(0),
nonce
);
}
/**
* @notice Create a new drop collection contract with a custom payment address derived from the factory.
* @dev All params other than `paymentAddressFactoryCall` are the same as in `createNFTDropCollection`.
* The nonce must be unique for the msg.sender + implementation version, otherwise this call will revert.
* @param name The collection's `name`.
* @param symbol The collection's `symbol`.
* @param baseURI The base URI for the collection.
* @param postRevealBaseURIHash The hash of the revealed baseURI for the collection.
* Set to bytes32(0) if the content is revealed by default (note that revealed content is immutable).
* If the post reveal content is unknown, use bytes32(uint(1)) to indicate the `baseURI` is pre-reveal content.
* @param maxTokenId The max token id for this collection.
* @param approvedMinter An optional address to grant the MINTER_ROLE.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @param paymentAddressFactoryCall The contract call which will return the address to use for payments.
* @return collection The address of the newly created collection contract.
*/
function createNFTDropCollectionWithPaymentFactory(
string calldata name,
string calldata symbol,
string calldata baseURI,
bytes32 postRevealBaseURIHash,
uint32 maxTokenId,
address approvedMinter,
uint256 nonce,
CallWithoutValue memory paymentAddressFactoryCall
) external returns (address collection) {
return
_createNFTDropCollection(
name,
symbol,
baseURI,
postRevealBaseURIHash,
maxTokenId,
approvedMinter,
AddressLibrary.callAndReturnContractAddress(paymentAddressFactoryCall),
nonce
);
}
function _createNFTDropCollection(
string calldata name,
string calldata symbol,
string calldata baseURI,
bytes32 postRevealBaseURIHash,
uint32 maxTokenId,
address approvedMinter,
address payable paymentAddress,
uint256 nonce
) private returns (address collection) {
// This reverts if the NFT was previously created using this implementation version + msg.sender + nonce
collection = implementationNFTDropCollection.cloneDeterministic(_getSalt(msg.sender, nonce));
INFTDropCollectionInitializer(collection).initialize(
payable(msg.sender),
name,
symbol,
baseURI,
postRevealBaseURIHash,
maxTokenId,
approvedMinter,
paymentAddress
);
emit NFTDropCollectionCreated(
collection,
msg.sender,
approvedMinter,
name,
symbol,
baseURI,
postRevealBaseURIHash,
maxTokenId,
paymentAddress,
versionNFTDropCollection,
nonce
);
}
/**
* @notice Returns the address of a collection given the current implementation version, creator, and nonce.
* This will return the same address whether the collection has already been created or not.
* @param creator The creator of the collection.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @return collection The address of the collection contract that would be created by this nonce.
*/
function predictNFTCollectionAddress(address creator, uint256 nonce) external view returns (address collection) {
collection = implementationNFTCollection.predictDeterministicAddress(_getSalt(creator, nonce));
}
/**
* @notice Returns the address of a NFTDropCollection collection given the current
* implementation version, creator, and nonce.
* This will return the same address whether the collection has already been created or not.
* @param creator The creator of the collection.
* @param nonce An arbitrary value used to allow a creator to mint multiple collections with a counterfactual address.
* @return collection The address of the collection contract that would be created by this nonce.
*/
function predictNFTDropCollectionAddress(address creator, uint256 nonce) external view returns (address collection) {
collection = implementationNFTDropCollection.predictDeterministicAddress(_getSalt(creator, nonce));
}
function _getSalt(address creator, uint256 nonce) private pure returns (bytes32) {
return keccak256(abi.encodePacked(creator, nonce));
}
}