@artblocks/contracts 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/.env.example +8 -1
  2. package/README.md +44 -165
  3. package/artifacts/contracts/AdminACLV1.sol/AdminACLV1.json +2 -2
  4. package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +2 -2
  5. package/artifacts/contracts/GenArt721CoreV3.sol/GenArt721CoreV3.json +2 -2
  6. package/artifacts/contracts/GenArt721CoreV3_Engine.sol/GenArt721CoreV3_Engine.json +2 -2
  7. package/artifacts/contracts/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +2229 -0
  8. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV0.sol/MinterDAExpV0.json +2 -2
  9. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV1.sol/MinterDAExpV1.json +2 -2
  10. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV2.sol/MinterDAExpV2.json +2 -2
  11. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV3.sol/MinterDAExpV3.json +2 -2
  12. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol/MinterDAExpSettlementV0.json +2 -2
  13. package/artifacts/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDAExpSettlement}/MinterDAExpSettlementV1.sol/MinterDAExpSettlementV1.json +3 -3
  14. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV0.sol/MinterDALinV0.json +2 -2
  15. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV1.sol/MinterDALinV1.json +2 -2
  16. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV2.sol/MinterDALinV2.json +2 -2
  17. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV3.sol/MinterDALinV3.json +2 -2
  18. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV0.sol/MinterHolderV0.json +2 -2
  19. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV1.sol/MinterHolderV1.json +2 -2
  20. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV2.sol/MinterHolderV2.json +2 -2
  21. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV3.sol/MinterHolderV3.json +2 -2
  22. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV0.sol/MinterMerkleV0.json +2 -2
  23. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol/MinterMerkleV1.json +2 -2
  24. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV2.sol/MinterMerkleV2.json +2 -2
  25. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV3.sol/MinterMerkleV3.json +2 -2
  26. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV4.sol/MinterMerkleV4.json +2 -2
  27. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV0.sol/MinterSetPriceV0.json +2 -2
  28. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV1.sol/MinterSetPriceV1.json +2 -2
  29. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV2.sol/MinterSetPriceV2.json +2 -2
  30. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV3.sol/MinterSetPriceV3.json +2 -2
  31. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V0.sol/MinterSetPriceERC20V0.json +2 -2
  32. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V1.sol/MinterSetPriceERC20V1.json +2 -2
  33. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V2.sol/MinterSetPriceERC20V2.json +2 -2
  34. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V3.sol/MinterSetPriceERC20V3.json +2 -2
  35. package/artifacts/contracts/explorations/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +2 -2
  36. package/artifacts/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlementV1.sol/IFilteredMinterDAExpSettlementV1.json +767 -0
  37. package/artifacts/contracts/interfaces/0.8.x/IFilteredMinterSEAV0.sol/IFilteredMinterSEAV0.json +1032 -0
  38. package/artifacts/contracts/interfaces/0.8.x/IGenArt721CoreContractV3.sol/IGenArt721CoreContractV3.json +149 -0
  39. package/artifacts/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Base.sol/IGenArt721CoreContractV3_Base.json +149 -0
  40. package/artifacts/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Engine.sol/IGenArt721CoreContractV3_Engine.json +130 -0
  41. package/artifacts/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol/IGenArt721CoreContractV3_Engine_Flex.json +1066 -0
  42. package/artifacts/contracts/interfaces/0.8.x/IMinterFilterV0.sol/IMinterFilterV0.json +6 -0
  43. package/artifacts/contracts/interfaces/0.8.x/IRandomizerPolyptychV0.sol/IRandomizerPolyptychV0.json +87 -0
  44. package/artifacts/contracts/interfaces/0.8.x/IRandomizerV2.sol/IRandomizerV2.json +1 -1
  45. package/artifacts/contracts/interfaces/0.8.x/IWETH.sol/IWETH.json +55 -0
  46. package/artifacts/contracts/libs/integration-refs/weth9/weth9.sol/WETH9_.json +295 -0
  47. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV0.sol/MinterFilterV0.json +8 -2
  48. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV1.sol/MinterFilterV1.json +34 -2
  49. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +1127 -0
  50. package/artifacts/contracts/minter-suite/Minters/MinterDAExpV4.sol/MinterDAExpV4.json +15 -2
  51. package/artifacts/contracts/minter-suite/Minters/MinterDALinV4.sol/MinterDALinV4.json +15 -2
  52. package/artifacts/contracts/minter-suite/Minters/MinterHolderV4.sol/MinterHolderV4.json +15 -2
  53. package/artifacts/contracts/minter-suite/Minters/MinterMerkleV5.sol/MinterMerkleV5.json +15 -2
  54. package/artifacts/contracts/minter-suite/Minters/MinterPolyptychV0.sol/IGenArt721CoreContractV3WithRandomizer.json +792 -0
  55. package/artifacts/contracts/minter-suite/Minters/MinterPolyptychV0.sol/MinterPolyptychV0.json +1268 -0
  56. package/artifacts/contracts/minter-suite/Minters/MinterSEAV0.sol/MinterSEAV0.json +1310 -0
  57. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V4.sol/MinterSetPriceERC20V4.json +15 -2
  58. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV4.sol/MinterSetPriceV4.json +15 -2
  59. package/artifacts/contracts/mock/DeadReceiverBidderMock.sol/DeadReceiverBidderMock.json +33 -0
  60. package/artifacts/contracts/mock/GasLimitReceiverBidderMock.sol/GasLimitReceiverBidderMock.json +46 -0
  61. package/artifacts/contracts/mock/GasLimitReceiverMock.sol/GasLimitReceiverMock.json +28 -0
  62. package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +2 -2
  63. package/artifacts/contracts/mock/RandomizerV2_NoAssignMock.sol/RandomizerV2_NoAssignMock.json +3 -3
  64. package/artifacts/contracts/mock/ReentrancySEAMock.sol/ReentrancySEAAutoBidderMock.json +51 -0
  65. package/artifacts/contracts/{BasicRandomizer.sol → randomizer/BasicRandomizer.sol}/BasicRandomizer.json +3 -3
  66. package/artifacts/contracts/randomizer/BasicRandomizerBase_v0_0_0.sol/RandomizerBase.json +10 -0
  67. package/artifacts/contracts/randomizer/PolyptychRandomizerV0.sol/BasicPolyptychRandomizerV0.json +229 -0
  68. package/artifacts/contracts/randomizer/RandomizerV2.sol/BasicRandomizerV2.json +102 -0
  69. package/artifacts/contracts/royalty-registry/GenArt721RoyaltyOverride.sol/GenArt721RoyaltyOverride.json +2 -2
  70. package/contracts/DependencyRegistryV0.sol +74 -113
  71. package/contracts/GenArt721CoreV3.sol +124 -171
  72. package/contracts/GenArt721CoreV3_Engine.sol +136 -198
  73. package/contracts/GenArt721CoreV3_Engine_Flex.sol +2274 -0
  74. package/contracts/{minter-suite/Minters → archive/minter-suite/Minters/MinterDAExpSettlement}/MinterDAExpSettlementV1.sol +4 -4
  75. package/contracts/explorations/GenArt721CoreV3_Explorations.sol +147 -176
  76. package/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlementV1.sol +21 -0
  77. package/contracts/interfaces/0.8.x/IFilteredMinterSEAV0.sol +145 -0
  78. package/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Base.sol +39 -0
  79. package/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Engine.sol +0 -3
  80. package/contracts/interfaces/0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol +132 -0
  81. package/contracts/interfaces/0.8.x/IMinterFilterV0.sol +6 -0
  82. package/contracts/interfaces/0.8.x/IRandomizerPolyptychV0.sol +31 -0
  83. package/contracts/interfaces/0.8.x/IRandomizerV2.sol +5 -2
  84. package/contracts/interfaces/0.8.x/IWETH.sol +11 -0
  85. package/contracts/libs/integration-refs/weth9/weth9.sol +763 -0
  86. package/contracts/minter-suite/MinterFilter/MinterFilterV1.sol +53 -47
  87. package/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol +1304 -0
  88. package/contracts/minter-suite/Minters/MinterDAExpV4.sol +47 -24
  89. package/contracts/minter-suite/Minters/MinterDALinV4.sol +46 -21
  90. package/contracts/minter-suite/Minters/MinterHolderV4.sol +54 -25
  91. package/contracts/minter-suite/Minters/MinterMerkleV5.sol +47 -20
  92. package/contracts/minter-suite/Minters/MinterPolyptychV0.sol +998 -0
  93. package/contracts/minter-suite/Minters/MinterSEAV0.sol +1224 -0
  94. package/contracts/minter-suite/Minters/MinterSetPriceERC20V4.sol +45 -16
  95. package/contracts/minter-suite/Minters/MinterSetPriceV4.sol +40 -13
  96. package/contracts/mock/DeadReceiverBidderMock.sol +23 -0
  97. package/contracts/mock/GasLimitReceiverBidderMock.sol +23 -0
  98. package/contracts/mock/GasLimitReceiverMock.sol +19 -0
  99. package/contracts/mock/RandomizerV2_NoAssignMock.sol +2 -11
  100. package/contracts/mock/ReentrancySEAMock.sol +36 -0
  101. package/contracts/{BasicRandomizer.sol → randomizer/BasicRandomizer.sol} +1 -1
  102. package/contracts/randomizer/BasicRandomizerBase_v0_0_0.sol +23 -0
  103. package/contracts/randomizer/PolyptychRandomizerV0.sol +117 -0
  104. package/contracts/{BasicRandomizerV2.sol → randomizer/RandomizerV2.sol} +7 -15
  105. package/package.json +32 -13
  106. package/artifacts/contracts/BasicRandomizerV2.sol/BasicRandomizerV2.json +0 -102
@@ -67,6 +67,9 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
67
67
  /// minterType for this minter
68
68
  string public constant minterType = "MinterDAExpV4";
69
69
 
70
+ /// minter version for this minter
71
+ string public constant minterVersion = "v4.1.0";
72
+
70
73
  uint256 constant ONE_MILLION = 1_000_000;
71
74
 
72
75
  struct ProjectConfig {
@@ -88,9 +91,9 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
88
91
  /// more than this amount (may cut in half at no more than every N seconds).
89
92
  uint256 public maximumPriceDecayHalfLifeSeconds = 3600; // 60 minutes
90
93
 
91
- // modifier to restrict access to only AdminACL allowed calls
94
+ // function to restrict access to only AdminACL allowed calls
92
95
  // @dev defers which ACL contract is used to the core contract
93
- modifier onlyCoreAdminACL(bytes4 _selector) {
96
+ function _onlyCoreAdminACL(bytes4 _selector) internal {
94
97
  require(
95
98
  genArtCoreContract_Base.adminACLAllowed(
96
99
  msg.sender,
@@ -99,16 +102,14 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
99
102
  ),
100
103
  "Only Core AdminACL allowed"
101
104
  );
102
- _;
103
105
  }
104
106
 
105
- modifier onlyArtist(uint256 _projectId) {
107
+ function _onlyArtist(uint256 _projectId) internal view {
106
108
  require(
107
109
  (msg.sender ==
108
110
  genArtCoreContract_Base.projectIdToArtistAddress(_projectId)),
109
111
  "Only Artist"
110
112
  );
111
- _;
112
113
  }
113
114
 
114
115
  /**
@@ -145,9 +146,8 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
145
146
  * @dev this enables gas reduction after maxInvocations have been reached -
146
147
  * core contracts shall still enforce a maxInvocation check during mint.
147
148
  */
148
- function setProjectMaxInvocations(
149
- uint256 _projectId
150
- ) external onlyArtist(_projectId) {
149
+ function setProjectMaxInvocations(uint256 _projectId) public {
150
+ _onlyArtist(_projectId);
151
151
  uint256 maxInvocations;
152
152
  uint256 invocations;
153
153
  (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
@@ -180,7 +180,8 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
180
180
  function manuallyLimitProjectMaxInvocations(
181
181
  uint256 _projectId,
182
182
  uint256 _maxInvocations
183
- ) external onlyArtist(_projectId) {
183
+ ) external {
184
+ _onlyArtist(_projectId);
184
185
  // CHECKS
185
186
  // ensure that the manually set maxInvocations is not greater than what is set on the core contract
186
187
  uint256 maxInvocations;
@@ -210,9 +211,8 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
210
211
  * @notice Warning: Disabling purchaseTo is not supported on this minter.
211
212
  * This method exists purely for interface-conformance purposes.
212
213
  */
213
- function togglePurchaseToDisabled(
214
- uint256 _projectId
215
- ) external view onlyArtist(_projectId) {
214
+ function togglePurchaseToDisabled(uint256 _projectId) external view {
215
+ _onlyArtist(_projectId);
216
216
  revert("Action not supported");
217
217
  }
218
218
 
@@ -295,12 +295,10 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
295
295
  function setAllowablePriceDecayHalfLifeRangeSeconds(
296
296
  uint256 _minimumPriceDecayHalfLifeSeconds,
297
297
  uint256 _maximumPriceDecayHalfLifeSeconds
298
- )
299
- external
300
- onlyCoreAdminACL(
298
+ ) external {
299
+ _onlyCoreAdminACL(
301
300
  this.setAllowablePriceDecayHalfLifeRangeSeconds.selector
302
- )
303
- {
301
+ );
304
302
  require(
305
303
  _maximumPriceDecayHalfLifeSeconds >
306
304
  _minimumPriceDecayHalfLifeSeconds,
@@ -336,7 +334,8 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
336
334
  uint256 _priceDecayHalfLifeSeconds,
337
335
  uint256 _startPrice,
338
336
  uint256 _basePrice
339
- ) external onlyArtist(_projectId) {
337
+ ) external {
338
+ _onlyArtist(_projectId);
340
339
  // CHECKS
341
340
  ProjectConfig storage _projectConfig = projectConfig[_projectId];
342
341
  require(
@@ -372,6 +371,16 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
372
371
  _startPrice,
373
372
  _basePrice
374
373
  );
374
+
375
+ // sync local max invocations if not initially populated
376
+ // @dev if local max invocations and maxHasBeenInvoked are both
377
+ // initial values, we know they have not been populated.
378
+ if (
379
+ _projectConfig.maxInvocations == 0 &&
380
+ _projectConfig.maxHasBeenInvoked == false
381
+ ) {
382
+ setProjectMaxInvocations(_projectId);
383
+ }
375
384
  }
376
385
 
377
386
  /**
@@ -380,9 +389,8 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
380
389
  * operation, but rather only in case of the need to halt an auction.
381
390
  * @param _projectId Project ID to set auction details for.
382
391
  */
383
- function resetAuctionDetails(
384
- uint256 _projectId
385
- ) external onlyCoreAdminACL(this.resetAuctionDetails.selector) {
392
+ function resetAuctionDetails(uint256 _projectId) external {
393
+ _onlyCoreAdminACL(this.resetAuctionDetails.selector);
386
394
  ProjectConfig storage _projectConfig = projectConfig[_projectId];
387
395
  // reset to initial values
388
396
  _projectConfig.timestampStart = 0;
@@ -460,10 +468,25 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
460
468
  // EFFECTS
461
469
  tokenId = minterFilter.mint(_to, _projectId, msg.sender);
462
470
 
463
- // okay if this underflows because if statement will always eval false.
464
- // this is only for gas optimization (core enforces maxInvocations).
471
+ // invocation is token number plus one, and will never overflow due to
472
+ // limit of 1e6 invocations per project. block scope for gas efficiency
473
+ // (i.e. avoid an unnecessary var initialization to 0).
465
474
  unchecked {
466
- if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
475
+ uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
476
+ uint256 localMaxInvocations = _projectConfig.maxInvocations;
477
+ // handle the case where the token invocation == minter local max
478
+ // invocations occurred on a different minter, and we have a stale
479
+ // local maxHasBeenInvoked value returning a false negative.
480
+ // @dev this is a CHECK after EFFECTS, so security was considered
481
+ // in detail here.
482
+ require(
483
+ tokenInvocation <= localMaxInvocations,
484
+ "Maximum invocations reached"
485
+ );
486
+ // in typical case, update the local maxHasBeenInvoked value
487
+ // to true if the token invocation == minter local max invocations
488
+ // (enables gas efficient reverts after sellout)
489
+ if (tokenInvocation == localMaxInvocations) {
467
490
  _projectConfig.maxHasBeenInvoked = true;
468
491
  }
469
492
  }
@@ -67,6 +67,9 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
67
67
  /// minterType for this minter
68
68
  string public constant minterType = "MinterDALinV4";
69
69
 
70
+ /// minter version for this minter
71
+ string public constant minterVersion = "v4.1.0";
72
+
70
73
  uint256 constant ONE_MILLION = 1_000_000;
71
74
 
72
75
  struct ProjectConfig {
@@ -84,9 +87,9 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
84
87
  /// Minimum auction length in seconds
85
88
  uint256 public minimumAuctionLengthSeconds = 3600;
86
89
 
87
- // modifier to restrict access to only AdminACL allowed calls
90
+ // function to restrict access to only AdminACL allowed calls
88
91
  // @dev defers which ACL contract is used to the core contract
89
- modifier onlyCoreAdminACL(bytes4 _selector) {
92
+ function _onlyCoreAdminACL(bytes4 _selector) internal {
90
93
  require(
91
94
  genArtCoreContract_Base.adminACLAllowed(
92
95
  msg.sender,
@@ -95,16 +98,14 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
95
98
  ),
96
99
  "Only Core AdminACL allowed"
97
100
  );
98
- _;
99
101
  }
100
102
 
101
- modifier onlyArtist(uint256 _projectId) {
103
+ function _onlyArtist(uint256 _projectId) internal view {
102
104
  require(
103
105
  (msg.sender ==
104
106
  genArtCoreContract_Base.projectIdToArtistAddress(_projectId)),
105
107
  "Only Artist"
106
108
  );
107
- _;
108
109
  }
109
110
 
110
111
  /**
@@ -141,9 +142,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
141
142
  * @dev this enables gas reduction after maxInvocations have been reached -
142
143
  * core contracts shall still enforce a maxInvocation check during mint.
143
144
  */
144
- function setProjectMaxInvocations(
145
- uint256 _projectId
146
- ) external onlyArtist(_projectId) {
145
+ function setProjectMaxInvocations(uint256 _projectId) public {
146
+ _onlyArtist(_projectId);
147
147
  uint256 maxInvocations;
148
148
  uint256 invocations;
149
149
  (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
@@ -175,7 +175,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
175
175
  function manuallyLimitProjectMaxInvocations(
176
176
  uint256 _projectId,
177
177
  uint256 _maxInvocations
178
- ) external onlyArtist(_projectId) {
178
+ ) external {
179
+ _onlyArtist(_projectId);
179
180
  // CHECKS
180
181
  // ensure that the manually set maxInvocations is not greater than what is set on the core contract
181
182
  uint256 maxInvocations;
@@ -204,9 +205,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
204
205
  * @notice Warning: Disabling purchaseTo is not supported on this minter.
205
206
  * This method exists purely for interface-conformance purposes.
206
207
  */
207
- function togglePurchaseToDisabled(
208
- uint256 _projectId
209
- ) external view onlyArtist(_projectId) {
208
+ function togglePurchaseToDisabled(uint256 _projectId) external view {
209
+ _onlyArtist(_projectId);
210
210
  revert("Action not supported");
211
211
  }
212
212
 
@@ -284,7 +284,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
284
284
  */
285
285
  function setMinimumAuctionLengthSeconds(
286
286
  uint256 _minimumAuctionLengthSeconds
287
- ) external onlyCoreAdminACL(this.setMinimumAuctionLengthSeconds.selector) {
287
+ ) external {
288
+ _onlyCoreAdminACL(this.setMinimumAuctionLengthSeconds.selector);
288
289
  minimumAuctionLengthSeconds = _minimumAuctionLengthSeconds;
289
290
  emit MinimumAuctionLengthSecondsUpdated(_minimumAuctionLengthSeconds);
290
291
  }
@@ -306,7 +307,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
306
307
  uint256 _auctionTimestampEnd,
307
308
  uint256 _startPrice,
308
309
  uint256 _basePrice
309
- ) external onlyArtist(_projectId) {
310
+ ) external {
311
+ _onlyArtist(_projectId);
310
312
  // CHECKS
311
313
  ProjectConfig storage _projectConfig = projectConfig[_projectId];
312
314
  require(
@@ -343,6 +345,16 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
343
345
  _startPrice,
344
346
  _basePrice
345
347
  );
348
+
349
+ // sync local max invocations if not initially populated
350
+ // @dev if local max invocations and maxHasBeenInvoked are both
351
+ // initial values, we know they have not been populated.
352
+ if (
353
+ _projectConfig.maxInvocations == 0 &&
354
+ _projectConfig.maxHasBeenInvoked == false
355
+ ) {
356
+ setProjectMaxInvocations(_projectId);
357
+ }
346
358
  }
347
359
 
348
360
  /**
@@ -351,9 +363,8 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
351
363
  * operation, but rather only in case of the need to halt an auction.
352
364
  * @param _projectId Project ID to set auction details for.
353
365
  */
354
- function resetAuctionDetails(
355
- uint256 _projectId
356
- ) external onlyCoreAdminACL(this.resetAuctionDetails.selector) {
366
+ function resetAuctionDetails(uint256 _projectId) external {
367
+ _onlyCoreAdminACL(this.resetAuctionDetails.selector);
357
368
  ProjectConfig storage _projectConfig = projectConfig[_projectId];
358
369
  // reset to initial values
359
370
  _projectConfig.timestampStart = 0;
@@ -430,11 +441,25 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
430
441
 
431
442
  // EFFECTS
432
443
  tokenId = minterFilter.mint(_to, _projectId, msg.sender);
433
-
434
- // okay if this underflows because if statement will always eval false.
435
- // this is only for gas optimization (core enforces maxInvocations).
444
+ // invocation is token number plus one, and will never overflow due to
445
+ // limit of 1e6 invocations per project. block scope for gas efficiency
446
+ // (i.e. avoid an unnecessary var initialization to 0).
436
447
  unchecked {
437
- if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
448
+ uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
449
+ uint256 localMaxInvocations = _projectConfig.maxInvocations;
450
+ // handle the case where the token invocation == minter local max
451
+ // invocations occurred on a different minter, and we have a stale
452
+ // local maxHasBeenInvoked value returning a false negative.
453
+ // @dev this is a CHECK after EFFECTS, so security was considered
454
+ // in detail here.
455
+ require(
456
+ tokenInvocation <= localMaxInvocations,
457
+ "Maximum invocations reached"
458
+ );
459
+ // in typical case, update the local maxHasBeenInvoked value
460
+ // to true if the token invocation == minter local max invocations
461
+ // (enables gas efficient reverts after sellout)
462
+ if (tokenInvocation == localMaxInvocations) {
438
463
  _projectConfig.maxHasBeenInvoked = true;
439
464
  }
440
465
  }
@@ -89,6 +89,9 @@ contract MinterHolderV4 is
89
89
  /// minterType for this minter
90
90
  string public constant minterType = "MinterHolderV4";
91
91
 
92
+ /// minter version for this minter
93
+ string public constant minterVersion = "v4.1.0";
94
+
92
95
  uint256 constant ONE_MILLION = 1_000_000;
93
96
 
94
97
  struct ProjectConfig {
@@ -110,9 +113,9 @@ contract MinterHolderV4 is
110
113
  mapping(uint256 => mapping(address => mapping(uint256 => bool)))
111
114
  public allowedProjectHolders;
112
115
 
113
- // modifier to restrict access to only AdminACL allowed calls
116
+ // function to restrict access to only AdminACL allowed calls
114
117
  // @dev defers which ACL contract is used to the core contract
115
- modifier onlyCoreAdminACL(bytes4 _selector) {
118
+ function _onlyCoreAdminACL(bytes4 _selector) internal {
116
119
  require(
117
120
  genArtCoreContract_Base.adminACLAllowed(
118
121
  msg.sender,
@@ -121,16 +124,14 @@ contract MinterHolderV4 is
121
124
  ),
122
125
  "Only Core AdminACL allowed"
123
126
  );
124
- _;
125
127
  }
126
128
 
127
- modifier onlyArtist(uint256 _projectId) {
129
+ function _onlyArtist(uint256 _projectId) internal view {
128
130
  require(
129
131
  msg.sender ==
130
132
  genArtCoreContract_Base.projectIdToArtistAddress(_projectId),
131
133
  "Only Artist"
132
134
  );
133
- _;
134
135
  }
135
136
 
136
137
  /**
@@ -174,9 +175,8 @@ contract MinterHolderV4 is
174
175
  * `projectId = tokenId / 1_000_000`
175
176
  * @param _NFTAddress NFT core address to be registered.
176
177
  */
177
- function registerNFTAddress(
178
- address _NFTAddress
179
- ) external onlyCoreAdminACL(this.registerNFTAddress.selector) {
178
+ function registerNFTAddress(address _NFTAddress) external {
179
+ _onlyCoreAdminACL(this.registerNFTAddress.selector);
180
180
  _registeredNFTAddresses.add(_NFTAddress);
181
181
  emit RegisteredNFTAddress(_NFTAddress);
182
182
  }
@@ -187,9 +187,8 @@ contract MinterHolderV4 is
187
187
  * considered for adding to future allowlists.
188
188
  * @param _NFTAddress NFT core address to be unregistered.
189
189
  */
190
- function unregisterNFTAddress(
191
- address _NFTAddress
192
- ) external onlyCoreAdminACL(this.unregisterNFTAddress.selector) {
190
+ function unregisterNFTAddress(address _NFTAddress) external {
191
+ _onlyCoreAdminACL(this.unregisterNFTAddress.selector);
193
192
  _registeredNFTAddresses.remove(_NFTAddress);
194
193
  emit UnregisteredNFTAddress(_NFTAddress);
195
194
  }
@@ -211,7 +210,8 @@ contract MinterHolderV4 is
211
210
  uint256 _projectId,
212
211
  address[] memory _ownedNFTAddresses,
213
212
  uint256[] memory _ownedNFTProjectIds
214
- ) public onlyArtist(_projectId) {
213
+ ) public {
214
+ _onlyArtist(_projectId);
215
215
  // require same length arrays
216
216
  require(
217
217
  _ownedNFTAddresses.length == _ownedNFTProjectIds.length,
@@ -256,7 +256,8 @@ contract MinterHolderV4 is
256
256
  uint256 _projectId,
257
257
  address[] memory _ownedNFTAddresses,
258
258
  uint256[] memory _ownedNFTProjectIds
259
- ) public onlyArtist(_projectId) {
259
+ ) public {
260
+ _onlyArtist(_projectId);
260
261
  // require same length arrays
261
262
  require(
262
263
  _ownedNFTAddresses.length == _ownedNFTProjectIds.length,
@@ -311,7 +312,8 @@ contract MinterHolderV4 is
311
312
  uint256[] memory _ownedNFTProjectIdsAdd,
312
313
  address[] memory _ownedNFTAddressesRemove,
313
314
  uint256[] memory _ownedNFTProjectIdsRemove
314
- ) external onlyArtist(_projectId) {
315
+ ) external {
316
+ _onlyArtist(_projectId);
315
317
  allowHoldersOfProjects(
316
318
  _projectId,
317
319
  _ownedNFTAddressesAdd,
@@ -352,7 +354,7 @@ contract MinterHolderV4 is
352
354
  * @dev this enables gas reduction after maxInvocations have been reached -
353
355
  * core contracts shall still enforce a maxInvocation check during mint.
354
356
  */
355
- function setProjectMaxInvocations(uint256 _projectId) external {
357
+ function setProjectMaxInvocations(uint256 _projectId) public {
356
358
  uint256 maxInvocations;
357
359
  uint256 invocations;
358
360
  (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
@@ -384,7 +386,8 @@ contract MinterHolderV4 is
384
386
  function manuallyLimitProjectMaxInvocations(
385
387
  uint256 _projectId,
386
388
  uint256 _maxInvocations
387
- ) external onlyArtist(_projectId) {
389
+ ) external {
390
+ _onlyArtist(_projectId);
388
391
  // CHECKS
389
392
  // ensure that the manually set maxInvocations is not greater than what is set on the core contract
390
393
  uint256 maxInvocations;
@@ -415,9 +418,8 @@ contract MinterHolderV4 is
415
418
  * @notice Warning: Disabling purchaseTo is not supported on this minter.
416
419
  * This method exists purely for interface-conformance purposes.
417
420
  */
418
- function togglePurchaseToDisabled(
419
- uint256 _projectId
420
- ) external view onlyArtist(_projectId) {
421
+ function togglePurchaseToDisabled(uint256 _projectId) external view {
422
+ _onlyArtist(_projectId);
421
423
  revert("Action not supported");
422
424
  }
423
425
 
@@ -474,10 +476,22 @@ contract MinterHolderV4 is
474
476
  function updatePricePerTokenInWei(
475
477
  uint256 _projectId,
476
478
  uint256 _pricePerTokenInWei
477
- ) external onlyArtist(_projectId) {
478
- projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
479
- projectConfig[_projectId].priceIsConfigured = true;
479
+ ) external {
480
+ _onlyArtist(_projectId);
481
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
482
+ _projectConfig.pricePerTokenInWei = _pricePerTokenInWei;
483
+ _projectConfig.priceIsConfigured = true;
480
484
  emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);
485
+
486
+ // sync local max invocations if not initially populated
487
+ // @dev if local max invocations and maxHasBeenInvoked are both
488
+ // initial values, we know they have not been populated.
489
+ if (
490
+ _projectConfig.maxInvocations == 0 &&
491
+ _projectConfig.maxHasBeenInvoked == false
492
+ ) {
493
+ setProjectMaxInvocations(_projectId);
494
+ }
481
495
  }
482
496
 
483
497
  /**
@@ -669,10 +683,25 @@ contract MinterHolderV4 is
669
683
 
670
684
  // NOTE: delegate-vault handling **ends here**.
671
685
 
672
- // okay if this underflows because if statement will always eval false.
673
- // this is only for gas optimization (core enforces maxInvocations).
686
+ // invocation is token number plus one, and will never overflow due to
687
+ // limit of 1e6 invocations per project. block scope for gas efficiency
688
+ // (i.e. avoid an unnecessary var initialization to 0).
674
689
  unchecked {
675
- if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
690
+ uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
691
+ uint256 localMaxInvocations = _projectConfig.maxInvocations;
692
+ // handle the case where the token invocation == minter local max
693
+ // invocations occurred on a different minter, and we have a stale
694
+ // local maxHasBeenInvoked value returning a false negative.
695
+ // @dev this is a CHECK after EFFECTS, so security was considered
696
+ // in detail here.
697
+ require(
698
+ tokenInvocation <= localMaxInvocations,
699
+ "Maximum invocations reached"
700
+ );
701
+ // in typical case, update the local maxHasBeenInvoked value
702
+ // to true if the token invocation == minter local max invocations
703
+ // (enables gas efficient reverts after sellout)
704
+ if (tokenInvocation == localMaxInvocations) {
676
705
  _projectConfig.maxHasBeenInvoked = true;
677
706
  }
678
707
  }
@@ -79,6 +79,9 @@ contract MinterMerkleV5 is
79
79
  /// minterType for this minter
80
80
  string public constant minterType = "MinterMerkleV5";
81
81
 
82
+ /// minter version for this minter
83
+ string public constant minterVersion = "v5.1.0";
84
+
82
85
  /// project minter configuration keys used by this minter
83
86
  bytes32 private constant CONFIG_MERKLE_ROOT = "merkleRoot";
84
87
  bytes32 private constant CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE =
@@ -112,13 +115,12 @@ contract MinterMerkleV5 is
112
115
  mapping(uint256 => mapping(address => uint256))
113
116
  public projectUserMintInvocations;
114
117
 
115
- modifier onlyArtist(uint256 _projectId) {
118
+ function _onlyArtist(uint256 _projectId) internal view {
116
119
  require(
117
120
  msg.sender ==
118
121
  genArtCoreContract_Base.projectIdToArtistAddress(_projectId),
119
122
  "Only Artist"
120
123
  );
121
- _;
122
124
  }
123
125
 
124
126
  /**
@@ -166,10 +168,8 @@ contract MinterMerkleV5 is
166
168
  * @param _root root of Merkle tree defining addresses allowed to mint
167
169
  * on project `_projectId`.
168
170
  */
169
- function updateMerkleRoot(
170
- uint256 _projectId,
171
- bytes32 _root
172
- ) external onlyArtist(_projectId) {
171
+ function updateMerkleRoot(uint256 _projectId, bytes32 _root) external {
172
+ _onlyArtist(_projectId);
173
173
  require(_root != bytes32(0), "Root must be provided");
174
174
  projectMerkleRoot[_projectId] = _root;
175
175
  emit ConfigValueSet(_projectId, CONFIG_MERKLE_ROOT, _root);
@@ -221,7 +221,8 @@ contract MinterMerkleV5 is
221
221
  function setProjectInvocationsPerAddress(
222
222
  uint256 _projectId,
223
223
  uint24 _maxInvocationsPerAddress
224
- ) external onlyArtist(_projectId) {
224
+ ) external {
225
+ _onlyArtist(_projectId);
225
226
  ProjectConfig storage _projectConfig = projectConfig[_projectId];
226
227
  // use override value instead of the contract's default
227
228
  // @dev this never changes from true to false; default value is only
@@ -250,9 +251,8 @@ contract MinterMerkleV5 is
250
251
  * @dev this enables gas reduction after maxInvocations have been reached -
251
252
  * core contracts shall still enforce a maxInvocation check during mint.
252
253
  */
253
- function setProjectMaxInvocations(
254
- uint256 _projectId
255
- ) external onlyArtist(_projectId) {
254
+ function setProjectMaxInvocations(uint256 _projectId) public {
255
+ _onlyArtist(_projectId);
256
256
  uint256 maxInvocations;
257
257
  uint256 invocations;
258
258
  (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
@@ -284,7 +284,8 @@ contract MinterMerkleV5 is
284
284
  function manuallyLimitProjectMaxInvocations(
285
285
  uint256 _projectId,
286
286
  uint256 _maxInvocations
287
- ) external onlyArtist(_projectId) {
287
+ ) external {
288
+ _onlyArtist(_projectId);
288
289
  // CHECKS
289
290
  // ensure that the manually set maxInvocations is not greater than what is set on the core contract
290
291
  uint256 maxInvocations;
@@ -315,9 +316,8 @@ contract MinterMerkleV5 is
315
316
  * @notice Warning: Disabling purchaseTo is not supported on this minter.
316
317
  * This method exists purely for interface-conformance purposes.
317
318
  */
318
- function togglePurchaseToDisabled(
319
- uint256 _projectId
320
- ) external view onlyArtist(_projectId) {
319
+ function togglePurchaseToDisabled(uint256 _projectId) external view {
320
+ _onlyArtist(_projectId);
321
321
  revert("Action not supported");
322
322
  }
323
323
 
@@ -374,10 +374,22 @@ contract MinterMerkleV5 is
374
374
  function updatePricePerTokenInWei(
375
375
  uint256 _projectId,
376
376
  uint256 _pricePerTokenInWei
377
- ) external onlyArtist(_projectId) {
378
- projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
379
- projectConfig[_projectId].priceIsConfigured = true;
377
+ ) external {
378
+ _onlyArtist(_projectId);
379
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
380
+ _projectConfig.pricePerTokenInWei = _pricePerTokenInWei;
381
+ _projectConfig.priceIsConfigured = true;
380
382
  emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);
383
+
384
+ // sync local max invocations if not initially populated
385
+ // @dev if local max invocations and maxHasBeenInvoked are both
386
+ // initial values, we know they have not been populated.
387
+ if (
388
+ _projectConfig.maxInvocations == 0 &&
389
+ _projectConfig.maxHasBeenInvoked == false
390
+ ) {
391
+ setProjectMaxInvocations(_projectId);
392
+ }
381
393
  }
382
394
 
383
395
  /**
@@ -551,10 +563,25 @@ contract MinterMerkleV5 is
551
563
 
552
564
  // NOTE: delegate-vault handling **ends here**.
553
565
 
554
- // okay if this underflows because if statement will always eval false.
555
- // this is only for gas optimization (core enforces maxInvocations).
566
+ // invocation is token number plus one, and will never overflow due to
567
+ // limit of 1e6 invocations per project. block scope for gas efficiency
568
+ // (i.e. avoid an unnecessary var initialization to 0).
556
569
  unchecked {
557
- if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
570
+ uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
571
+ uint256 localMaxInvocations = _projectConfig.maxInvocations;
572
+ // handle the case where the token invocation == minter local max
573
+ // invocations occurred on a different minter, and we have a stale
574
+ // local maxHasBeenInvoked value returning a false negative.
575
+ // @dev this is a CHECK after EFFECTS, so security was considered
576
+ // in detail here.
577
+ require(
578
+ tokenInvocation <= localMaxInvocations,
579
+ "Maximum invocations reached"
580
+ );
581
+ // in typical case, update the local maxHasBeenInvoked value
582
+ // to true if the token invocation == minter local max invocations
583
+ // (enables gas efficient reverts after sellout)
584
+ if (tokenInvocation == localMaxInvocations) {
558
585
  _projectConfig.maxHasBeenInvoked = true;
559
586
  }
560
587
  }