-
Notifications
You must be signed in to change notification settings - Fork 26
/
MinterCreatorSharedRoyalties.sol
382 lines (322 loc) · 15.1 KB
/
MinterCreatorSharedRoyalties.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "./helpers/IPaymentSplitterInitializable.sol";
import "../access/OwnablePermissions.sol";
import "@openzeppelin/contracts/interfaces/IERC2981.sol";
import "@openzeppelin/contracts/proxy/Clones.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
/**
* @title MinterCreatorSharedRoyaltiesBase
* @author Limit Break, Inc.
* @dev Base functionality of an NFT mix-in contract implementing programmable royalties. Royalties are shared between creators and minters.
*/
abstract contract MinterCreatorSharedRoyaltiesBase is IERC2981, ERC165 {
error MinterCreatorSharedRoyalties__CreatorCannotBeZeroAddress();
error MinterCreatorSharedRoyalties__CreatorSharesCannotBeZero();
error MinterCreatorSharedRoyalties__MinterCannotBeZeroAddress();
error MinterCreatorSharedRoyalties__MinterHasAlreadyBeenAssignedToTokenId();
error MinterCreatorSharedRoyalties__MinterSharesCannotBeZero();
error MinterCreatorSharedRoyalties__PaymentSplitterDoesNotExistForSpecifiedTokenId();
error MinterCreatorSharedRoyalties__PaymentSplitterReferenceCannotBeZeroAddress();
error MinterCreatorSharedRoyalties__RoyaltyFeeWillExceedSalePrice();
enum ReleaseTo {
Minter,
Creator
}
uint256 public constant FEE_DENOMINATOR = 10_000;
uint256 private _royaltyFeeNumerator;
uint256 private _minterShares;
uint256 private _creatorShares;
address private _creator;
address private _paymentSplitterReference;
mapping (uint256 => address) private _minters;
mapping (uint256 => address) private _paymentSplitters;
mapping (address => address[]) private _minterPaymentSplitters;
/**
* @notice Indicates whether the contract implements the specified interface.
* @dev Overrides supportsInterface in ERC165.
* @param interfaceId The interface id
* @return true if the contract implements the specified interface, false otherwise
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) {
return interfaceId == type(IERC2981).interfaceId || super.supportsInterface(interfaceId);
}
function royaltyFeeNumerator() public virtual view returns (uint256) {
return _royaltyFeeNumerator;
}
function minterShares() public virtual view returns (uint256) {
return _minterShares;
}
function creatorShares() public virtual view returns (uint256) {
return _creatorShares;
}
function creator() public virtual view returns (address) {
return _creator;
}
function paymentSplitterReference() public virtual view returns (address) {
return _paymentSplitterReference;
}
/**
* @notice Returns the royalty fee and recipient for a given token.
* @param tokenId The id of the token whose royalty info is being queried.
* @param salePrice The sale price of the token.
* @return The royalty fee and recipient for a given token.
*/
function royaltyInfo(
uint256 tokenId,
uint256 salePrice
) external view override returns (address, uint256) {
return (_paymentSplitters[tokenId], (salePrice * royaltyFeeNumerator()) / FEE_DENOMINATOR);
}
/**
* @notice Returns the minter of the token with id `tokenId`.
* @param tokenId The id of the token whose minter is being queried.
* @return The minter of the token with id `tokenId`.
*/
function minterOf(uint256 tokenId) external view returns (address) {
return _minters[tokenId];
}
/**
* @notice Returns the payment splitter of the token with id `tokenId`.
* @param tokenId The id of the token whose payment splitter is being queried.
* @return The payment splitter of the token with id `tokenId`.
*/
function paymentSplitterOf(uint256 tokenId) external view returns (address) {
return _paymentSplitters[tokenId];
}
/**
* @notice Returns the payment splitters of the minter `minter`.
* @param minter The minter whose payment splitters are being queried.
* @return The payment splitters of the minter `minter`.
*/
function paymentSplittersOfMinter(address minter) external view returns (address[] memory) {
return _minterPaymentSplitters[minter];
}
/**
* @notice Returns the amount of native funds that can be released to the minter or creator of the token with id `tokenId`.
* @param tokenId The id of the token whose releasable funds are being queried.
* @param releaseTo Specifies whether the minter or creator should be queried.
* @return The amount of native funds that can be released to the minter or creator of the token with id `tokenId`.
*/
function releasableNativeFunds(uint256 tokenId, ReleaseTo releaseTo) external view returns (uint256) {
IPaymentSplitterInitializable paymentSplitter = _getPaymentSplitterForTokenOrRevert(tokenId);
if (releaseTo == ReleaseTo.Minter) {
return paymentSplitter.releasable(payable(_minters[tokenId]));
} else {
return paymentSplitter.releasable(payable(creator()));
}
}
/**
* @notice Returns the amount of ERC20 funds that can be released to the minter or creator of the token with id `tokenId`.
* @param tokenId The id of the token whose releasable funds are being queried.
* @param coin The address of the ERC20 token whose releasable funds are being queried.
* @param releaseTo Specifies whether the minter or creator should be queried.
* @return The amount of ERC20 funds that can be released to the minter or creator of the token with id `tokenId`.
*/
function releasableERC20Funds(uint256 tokenId, address coin, ReleaseTo releaseTo) external view returns (uint256) {
IPaymentSplitterInitializable paymentSplitter = _getPaymentSplitterForTokenOrRevert(tokenId);
if (releaseTo == ReleaseTo.Minter) {
return paymentSplitter.releasable(IERC20(coin), _minters[tokenId]);
} else {
return paymentSplitter.releasable(IERC20(coin), creator());
}
}
/**
* @notice Releases all available native funds to the minter or creator of the token with id `tokenId`.
* @param tokenId The id of the token whose funds are being released.
* @param releaseTo Specifies whether the minter or creator should be released to.
*/
function releaseNativeFunds(uint256 tokenId, ReleaseTo releaseTo) external {
IPaymentSplitterInitializable paymentSplitter = _getPaymentSplitterForTokenOrRevert(tokenId);
if (releaseTo == ReleaseTo.Minter) {
paymentSplitter.release(payable(_minters[tokenId]));
} else {
paymentSplitter.release(payable(creator()));
}
}
/**
* @notice Releases all available ERC20 funds to the minter or creator of the token with id `tokenId`.
* @param tokenId The id of the token whose funds are being released.
* @param coin The address of the ERC20 token whose funds are being released.
* @param releaseTo Specifies whether the minter or creator should be released to.
*/
function releaseERC20Funds(uint256 tokenId, address coin, ReleaseTo releaseTo) external {
IPaymentSplitterInitializable paymentSplitter = _getPaymentSplitterForTokenOrRevert(tokenId);
if(releaseTo == ReleaseTo.Minter) {
paymentSplitter.release(IERC20(coin), _minters[tokenId]);
} else {
paymentSplitter.release(IERC20(coin), creator());
}
}
/**
* @dev Internal function that must be called when a token is minted.
* Creates a payment splitter for the minter and creator of the token to share royalties.
* @param minter The minter of the token.
* @param tokenId The id of the token that was minted.
*/
function _onMinted(address minter, uint256 tokenId) internal {
if (minter == address(0)) {
revert MinterCreatorSharedRoyalties__MinterCannotBeZeroAddress();
}
if (_minters[tokenId] != address(0)) {
revert MinterCreatorSharedRoyalties__MinterHasAlreadyBeenAssignedToTokenId();
}
address paymentSplitter = _createPaymentSplitter(minter);
_paymentSplitters[tokenId] = paymentSplitter;
_minterPaymentSplitters[minter].push(paymentSplitter);
_minters[tokenId] = minter;
}
/**
* @dev Internal function that must be called when a token is burned.
* Deletes the payment splitter mapping and minter mapping for the token in case it is ever re-minted.
* @param tokenId The id of the token that was burned.
*/
function _onBurned(uint256 tokenId) internal {
delete _paymentSplitters[tokenId];
delete _minters[tokenId];
}
/**
* @dev Internal function that creates a payment splitter for the minter and creator of the token to share royalties.
* @param minter The minter of the token.
* @return The address of the payment splitter.
*/
function _createPaymentSplitter(address minter) private returns (address) {
address creator_ = creator();
address paymentSplitterReference_ = paymentSplitterReference();
IPaymentSplitterInitializable paymentSplitter =
IPaymentSplitterInitializable(Clones.clone(paymentSplitterReference_));
if (minter == creator_) {
address[] memory payees = new address[](1);
payees[0] = creator_;
uint256[] memory shares = new uint256[](1);
shares[0] = minterShares() + creatorShares();
paymentSplitter.initializePaymentSplitter(payees, shares);
} else {
address[] memory payees = new address[](2);
payees[0] = minter;
payees[1] = creator_;
uint256[] memory shares = new uint256[](2);
shares[0] = minterShares();
shares[1] = creatorShares();
paymentSplitter.initializePaymentSplitter(payees, shares);
}
return address(paymentSplitter);
}
/**
* @dev Gets the payment splitter for the specified token id or reverts if it does not exist.
*/
function _getPaymentSplitterForTokenOrRevert(uint256 tokenId) private view returns (IPaymentSplitterInitializable) {
address paymentSplitterForToken = _paymentSplitters[tokenId];
if(paymentSplitterForToken == address(0)) {
revert MinterCreatorSharedRoyalties__PaymentSplitterDoesNotExistForSpecifiedTokenId();
}
return IPaymentSplitterInitializable(payable(paymentSplitterForToken));
}
function _setRoyaltyFeeNumeratorAndShares(
uint256 royaltyFeeNumerator_,
uint256 minterShares_,
uint256 creatorShares_,
address creator_,
address paymentSplitterReference_) internal {
if(royaltyFeeNumerator_ > FEE_DENOMINATOR) {
revert MinterCreatorSharedRoyalties__RoyaltyFeeWillExceedSalePrice();
}
if (minterShares_ == 0) {
revert MinterCreatorSharedRoyalties__MinterSharesCannotBeZero();
}
if (creatorShares_ == 0) {
revert MinterCreatorSharedRoyalties__CreatorSharesCannotBeZero();
}
if (creator_ == address(0)) {
revert MinterCreatorSharedRoyalties__CreatorCannotBeZeroAddress();
}
if (paymentSplitterReference_ == address(0)) {
revert MinterCreatorSharedRoyalties__PaymentSplitterReferenceCannotBeZeroAddress();
}
_royaltyFeeNumerator = royaltyFeeNumerator_;
_minterShares = minterShares_;
_creatorShares = creatorShares_;
_creator = creator_;
_paymentSplitterReference = paymentSplitterReference_;
}
}
/**
* @title MinterCreatorSharedRoyalties
* @author Limit Break, Inc.
* @notice Constructable MinterCreatorSharedRoyalties Contract implementation.
*/
abstract contract MinterCreatorSharedRoyalties is MinterCreatorSharedRoyaltiesBase {
uint256 private immutable _royaltyFeeNumeratorImmutable;
uint256 private immutable _minterSharesImmutable;
uint256 private immutable _creatorSharesImmutable;
address private immutable _creatorImmutable;
address private immutable _paymentSplitterReferenceImmutable;
/**
* @dev Constructor that sets the royalty fee numerator, creator, and minter/creator shares.
* @dev Throws when defaultRoyaltyFeeNumerator_ is greater than FEE_DENOMINATOR
* @param royaltyFeeNumerator_ The royalty fee numerator
* @param minterShares_ The number of shares minters get allocated in payment processors
* @param creatorShares_ The number of shares creators get allocated in payment processors
* @param creator_ The NFT creator's royalty wallet
*/
constructor(
uint256 royaltyFeeNumerator_,
uint256 minterShares_,
uint256 creatorShares_,
address creator_,
address paymentSplitterReference_) {
_setRoyaltyFeeNumeratorAndShares(
royaltyFeeNumerator_,
minterShares_,
creatorShares_,
creator_,
paymentSplitterReference_);
_royaltyFeeNumeratorImmutable = royaltyFeeNumerator_;
_minterSharesImmutable = minterShares_;
_creatorSharesImmutable = creatorShares_;
_creatorImmutable = creator_;
_paymentSplitterReferenceImmutable = paymentSplitterReference_;
}
function royaltyFeeNumerator() public view override returns (uint256) {
return _royaltyFeeNumeratorImmutable;
}
function minterShares() public view override returns (uint256) {
return _minterSharesImmutable;
}
function creatorShares() public view override returns (uint256) {
return _creatorSharesImmutable;
}
function creator() public view override returns (address) {
return _creatorImmutable;
}
function paymentSplitterReference() public view override returns (address) {
return _paymentSplitterReferenceImmutable;
}
}
/**
* @title MinterCreatorSharedRoyaltiesInitializable
* @author Limit Break, Inc.
* @notice Initializable MinterCreatorSharedRoyalties Contract implementation to allow for EIP-1167 clones.
*/
abstract contract MinterCreatorSharedRoyaltiesInitializable is OwnablePermissions, MinterCreatorSharedRoyaltiesBase {
error MinterCreatorSharedRoyaltiesInitializable__RoyaltyFeeAndSharesAlreadyInitialized();
bool private _royaltyFeeAndSharesInitialized;
function initializeMinterRoyaltyFee(
uint256 royaltyFeeNumerator_,
uint256 minterShares_,
uint256 creatorShares_,
address creator_,
address paymentSplitterReference_) public {
_requireCallerIsContractOwner();
if(_royaltyFeeAndSharesInitialized) {
revert MinterCreatorSharedRoyaltiesInitializable__RoyaltyFeeAndSharesAlreadyInitialized();
}
_royaltyFeeAndSharesInitialized = true;
_setRoyaltyFeeNumeratorAndShares(
royaltyFeeNumerator_,
minterShares_,
creatorShares_,
creator_,
paymentSplitterReference_);
}
}