@artblocks/contracts 1.0.4 → 1.1.0

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 (120) hide show
  1. package/README.md +2 -2
  2. package/artifacts/contracts/AdminACLV1.sol/AdminACLV1.json +2 -2
  3. package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +97 -10
  4. package/artifacts/contracts/GenArt721CoreV3.sol/GenArt721CoreV3.json +4 -4
  5. package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine.sol/GenArt721CoreV3_Engine.json +4 -9
  6. package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +4 -9
  7. package/artifacts/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol/GenArt721CoreV3_Engine_Flex_PROOF.json +4 -9
  8. package/artifacts/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol/GenArt721CoreV3_Engine_Flex_PROHIBITION.json +4 -9
  9. package/artifacts/contracts/engine-registry/CoreRegistryV1.sol/CoreRegistryV1.json +12 -12
  10. package/artifacts/contracts/explorations/GenArt721CoreV3_Explorations.sol/GenArt721CoreV3_Explorations.json +4 -4
  11. package/artifacts/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol/ICoreRegistryV1.json +6 -6
  12. package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +19 -0
  13. package/artifacts/contracts/interfaces/v0.8.x/IMinterFilterV1.sol/IMinterFilterV1.json +22 -22
  14. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol/ISharedMinterDAExpSettlementV0.json +8 -101
  15. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol/ISharedMinterDAExpV0.json +1 -57
  16. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol/ISharedMinterDALinV0.json +1 -57
  17. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol/ISharedMinterDAV0.json +2 -21
  18. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol/ISharedMinterHolderERC20V0.json +158 -0
  19. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol/ISharedMinterHolderV0.json +15 -90
  20. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol/ISharedMinterMerkleV0.json +10 -36
  21. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol/ISharedMinterSEAV0.json +4 -236
  22. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol/ISharedMinterSimplePurchaseV0.json +5 -5
  23. package/artifacts/contracts/interfaces/v0.8.x/ISharedMinterV0.sol/ISharedMinterV0.json +7 -423
  24. package/artifacts/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol/ISharedRandomizerV0.json +17 -17
  25. package/artifacts/contracts/libs/v0.8.x/ABHelpers.sol/ABHelpers.json +2 -2
  26. package/artifacts/contracts/libs/v0.8.x/AuthLib.sol/AuthLib.json +2 -2
  27. package/artifacts/contracts/libs/v0.8.x/Bytes32Strings.sol/Bytes32Strings.json +2 -2
  28. package/artifacts/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol/DAExpLib.json +60 -3
  29. package/artifacts/contracts/libs/v0.8.x/minter-libs/DALib.sol/DALib.json +30 -0
  30. package/artifacts/contracts/libs/v0.8.x/minter-libs/DALinLib.sol/DALinLib.json +60 -3
  31. package/artifacts/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol/GenericMinterEventsLib.json +346 -0
  32. package/artifacts/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol/MaxInvocationsLib.json +29 -3
  33. package/artifacts/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol/MerkleLib.json +30 -3
  34. package/artifacts/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol/PolyptychLib.json +2 -2
  35. package/artifacts/contracts/libs/v0.8.x/minter-libs/SEALib.sol/SEALib.json +254 -3
  36. package/artifacts/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol/SetPriceLib.json +48 -3
  37. package/artifacts/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol/SettlementExpLib.json +41 -3
  38. package/artifacts/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol/SplitFundsLib.json +35 -3
  39. package/artifacts/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol/TokenHolderLib.json +79 -3
  40. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV1.sol/MinterFilterV1.json +2 -2
  41. package/artifacts/contracts/minter-suite/MinterFilter/MinterFilterV2.sol/MinterFilterV2.json +41 -41
  42. package/artifacts/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol/MinterDAExpHolderV5.json +66 -632
  43. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol/MinterDAExpSettlementV2.json +2 -20
  44. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol/MinterDAExpSettlementV3.json +67 -594
  45. package/artifacts/contracts/minter-suite/Minters/MinterDAExpV5.sol/MinterDAExpV5.json +33 -524
  46. package/artifacts/contracts/minter-suite/Minters/MinterDALinHolderV5.sol/MinterDALinHolderV5.json +66 -632
  47. package/artifacts/contracts/minter-suite/Minters/MinterDALinV5.sol/MinterDALinV5.json +33 -524
  48. package/artifacts/contracts/minter-suite/Minters/MinterSEAV1.sol/MinterSEAV1.json +51 -717
  49. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol/MinterSetPriceERC20V5.json +58 -454
  50. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol/MinterSetPriceHolderV5.json +61 -552
  51. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol/MinterSetPriceMerkleV5.json +55 -497
  52. package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol/MinterSetPricePolyptychERC20V5.json +110 -571
  53. package/artifacts/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol/MinterSetPricePolyptychV5.json +69 -560
  54. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV5.sol/MinterSetPriceV5.json +28 -444
  55. package/artifacts/contracts/mock/DummySharedMinter.sol/DummySharedMinter.json +2 -2
  56. package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +4 -9
  57. package/artifacts/contracts/mock/OriginRegisterMock.sol/OriginRegisterMock.json +2 -2
  58. package/artifacts/contracts/mock/ReentrancyHolderMockShared.sol/ReentrancyHolderMockShared.json +136 -0
  59. package/artifacts/contracts/mock/ReentrancyMerkleMockShared.sol/ReentrancyMerkleMockShared.json +105 -0
  60. package/artifacts/contracts/mock/ReentrancyMockShared.sol/ReentrancyMockShared.json +100 -0
  61. package/artifacts/contracts/mock/ReentrancySEAMockShared.sol/ReentrancySEAAutoBidderMockShared.json +69 -0
  62. package/artifacts/contracts/randomizer/shared/SharedRandomizerV0.sol/SharedRandomizerV0.json +19 -19
  63. package/contracts/DependencyRegistryV0.sol +84 -16
  64. package/contracts/archive/readme.md +16 -0
  65. package/contracts/engine/README.md +21 -0
  66. package/contracts/engine/V3/GenArt721CoreV3_Engine.sol +4 -10
  67. package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +4 -10
  68. package/contracts/engine/V3/forks/GenArt721CoreV3_Engine_Flex_PROOF.sol +4 -10
  69. package/contracts/engine/V3/forks/PROHIBITION/GenArt721CoreV3_Engine_Flex_PROHIBITION.sol +4 -10
  70. package/contracts/engine-registry/CoreRegistryV1.sol +58 -65
  71. package/contracts/interfaces/v0.8.x/ICoreRegistryV1.sol +6 -8
  72. package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +3 -0
  73. package/contracts/interfaces/v0.8.x/IMinterFilterV1.sol +25 -25
  74. package/contracts/interfaces/v0.8.x/ISharedMinterDAExpSettlementV0.sol +11 -20
  75. package/contracts/interfaces/v0.8.x/ISharedMinterDAExpV0.sol +1 -14
  76. package/contracts/interfaces/v0.8.x/ISharedMinterDALinV0.sol +1 -15
  77. package/contracts/interfaces/v0.8.x/ISharedMinterDAV0.sol +2 -8
  78. package/contracts/interfaces/v0.8.x/ISharedMinterHolderERC20V0.sol +47 -0
  79. package/contracts/interfaces/v0.8.x/ISharedMinterHolderV0.sol +15 -43
  80. package/contracts/interfaces/v0.8.x/ISharedMinterMerkleV0.sol +10 -20
  81. package/contracts/interfaces/v0.8.x/ISharedMinterSEAV0.sol +4 -67
  82. package/contracts/interfaces/v0.8.x/ISharedMinterSimplePurchaseV0.sol +5 -5
  83. package/contracts/interfaces/v0.8.x/ISharedMinterV0.sol +7 -155
  84. package/contracts/interfaces/v0.8.x/ISharedRandomizerV0.sol +37 -37
  85. package/contracts/libs/v0.8.x/ABHelpers.sol +14 -14
  86. package/contracts/libs/v0.8.x/AuthLib.sol +101 -102
  87. package/contracts/libs/v0.8.x/Bytes32Strings.sol +5 -5
  88. package/contracts/libs/v0.8.x/minter-libs/DAExpLib.sol +163 -38
  89. package/contracts/libs/v0.8.x/minter-libs/DALib.sol +26 -0
  90. package/contracts/libs/v0.8.x/minter-libs/DALinLib.sol +144 -37
  91. package/contracts/libs/v0.8.x/minter-libs/GenericMinterEventsLib.sol +171 -0
  92. package/contracts/libs/v0.8.x/minter-libs/MaxInvocationsLib.sol +250 -110
  93. package/contracts/libs/v0.8.x/minter-libs/MerkleLib.sol +257 -48
  94. package/contracts/libs/v0.8.x/minter-libs/PolyptychLib.sol +138 -58
  95. package/contracts/libs/v0.8.x/minter-libs/SEALib.sol +778 -103
  96. package/contracts/libs/v0.8.x/minter-libs/SetPriceLib.sol +135 -13
  97. package/contracts/libs/v0.8.x/minter-libs/SettlementExpLib.sol +567 -187
  98. package/contracts/libs/v0.8.x/minter-libs/SplitFundsLib.sol +480 -322
  99. package/contracts/libs/v0.8.x/minter-libs/TokenHolderLib.sol +214 -76
  100. package/contracts/minter-suite/MinterFilter/MinterFilterV2.sol +347 -304
  101. package/contracts/minter-suite/Minters/MinterDAExpHolderV5.sol +391 -491
  102. package/contracts/minter-suite/Minters/MinterDAExpSettlementV2.sol +1 -54
  103. package/contracts/minter-suite/Minters/MinterDAExpSettlementV3.sol +388 -628
  104. package/contracts/minter-suite/Minters/MinterDAExpV5.sol +212 -273
  105. package/contracts/minter-suite/Minters/MinterDALinHolderV5.sol +393 -488
  106. package/contracts/minter-suite/Minters/MinterDALinV5.sol +220 -277
  107. package/contracts/minter-suite/Minters/MinterSEAV1.sol +327 -664
  108. package/contracts/minter-suite/Minters/MinterSetPriceERC20V5.sol +278 -303
  109. package/contracts/minter-suite/Minters/MinterSetPriceHolderV5.sol +355 -446
  110. package/contracts/minter-suite/Minters/MinterSetPriceMerkleV5.sol +328 -416
  111. package/contracts/minter-suite/Minters/MinterSetPricePolyptychERC20V5.sol +538 -622
  112. package/contracts/minter-suite/Minters/MinterSetPricePolyptychV5.sol +429 -553
  113. package/contracts/minter-suite/Minters/MinterSetPriceV5.sol +180 -227
  114. package/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol +3 -10
  115. package/contracts/mock/ReentrancyHolderMockShared.sol +59 -0
  116. package/contracts/mock/ReentrancyMerkleMockShared.sol +58 -0
  117. package/contracts/mock/ReentrancyMockShared.sol +48 -0
  118. package/contracts/mock/ReentrancySEAMockShared.sol +40 -0
  119. package/contracts/randomizer/shared/SharedRandomizerV0.sol +78 -70
  120. package/package.json +39 -33
@@ -1,13 +1,15 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-only
2
2
  // Created By: Art Blocks Inc.
3
3
 
4
- import "./MaxInvocationsLib.sol";
5
- import "./DAExpLib.sol";
6
- import "./SplitFundsLib.sol";
4
+ pragma solidity ^0.8.0;
7
5
 
8
- import "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";
6
+ import {MaxInvocationsLib} from "./MaxInvocationsLib.sol";
7
+ import {DAExpLib} from "./DAExpLib.sol";
8
+ import {SplitFundsLib} from "./SplitFundsLib.sol";
9
+ import {AuthLib} from "../AuthLib.sol";
10
+ import {GenericMinterEventsLib} from "./GenericMinterEventsLib.sol";
9
11
 
10
- pragma solidity ^0.8.0;
12
+ import {SafeCast} from "@openzeppelin-4.7/contracts/utils/math/SafeCast.sol";
11
13
 
12
14
  /**
13
15
  * @title Art Blocks Settlement Library for Exponential Auctions
@@ -20,6 +22,26 @@ pragma solidity ^0.8.0;
20
22
 
21
23
  library SettlementExpLib {
22
24
  using SafeCast for uint256;
25
+ /**
26
+ * @notice Receipt updated event. Emitted when a receipt is updated.
27
+ * @param purchaser purchaser address of updated receipt
28
+ * @param projectId project ID of updated receipt
29
+ * @param coreContract core contract address of updated receipt
30
+ * @param numPurchased new number of tokens purchased on project
31
+ * @param netPosted new net funds posted on project
32
+ */
33
+ event ReceiptUpdated(
34
+ address indexed purchaser,
35
+ uint256 indexed projectId,
36
+ address indexed coreContract,
37
+ uint24 numPurchased,
38
+ uint256 netPosted
39
+ );
40
+
41
+ // position of Settlement Exp Lib storage, using a diamond storage pattern
42
+ // for this library
43
+ bytes32 constant SETTLEMENT_EXP_LIB_STORAGE_POSITION =
44
+ keccak256("settlementExpLib.storage");
23
45
 
24
46
  bytes32 internal constant CONFIG_CURRENT_SETTLED_PRICE =
25
47
  "currentSettledPrice";
@@ -42,10 +64,30 @@ library SettlementExpLib {
42
64
  // In that case, the new auction will be required to have a starting
43
65
  // price less than or equal to this value, if one or more purchases
44
66
  // have been made on this minter.
45
- // @dev max uint128 allows for > 1e51 ETH (much more than max supply)
46
- // This enables struct packing, and is consistent with prices value
47
- // limits in DAExpLib's DAProjectConfig struct.
48
- uint128 latestPurchasePrice;
67
+ // @dev max uint88 ~= 3e26 Wei = ~300 million ETH, which is well above
68
+ // the expected prices of any NFT mint in the foreseeable future.
69
+ // This enables struct packing.
70
+ uint88 latestPurchasePrice;
71
+ // Track per-project fund balance, in wei. This is used as a redundant
72
+ // backstop to prevent one project from draining the minter's balance
73
+ // of ETH from other projects, which is a worthwhile failsafe on this
74
+ // shared minter.
75
+ // @dev max uint88 ~= 3e26 Wei = ~300 million ETH, which is well above
76
+ // the expected revenues for a single auction.
77
+ // This enables struct packing.
78
+ uint88 projectBalance;
79
+ // field to store the number of purchases that have been made on the
80
+ // project, on this minter. This is used to track if unexpected mints
81
+ // from sources other than this minter occur during an auction.
82
+ // @dev max uint24 allows for > max project supply of 1 million tokens
83
+ // @dev important to pack this field with other fields updated during a
84
+ // purchase, for gas efficiency
85
+ uint24 numPurchasesOnMinter;
86
+ // The number of tokens to be auctioned for the project on this minter.
87
+ // This is defined as the number of invocations remaining at the time
88
+ // of a project's first mint.
89
+ uint24 numTokensToBeAuctioned;
90
+ // --- @dev end of storage slot ---
49
91
  }
50
92
 
51
93
  // The Receipt struct tracks the state of a user's settlement on a given
@@ -58,101 +100,62 @@ library SettlementExpLib {
58
100
  uint24 numPurchased;
59
101
  }
60
102
 
103
+ // Diamond storage pattern is used in this library
104
+ struct SettlementExpLibStorage {
105
+ mapping(address coreContract => mapping(uint256 projectId => SettlementAuctionProjectConfig)) settlementAuctionProjectConfigs;
106
+ mapping(address walletAddress => mapping(address coreContract => mapping(uint256 projectId => Receipt))) receipts;
107
+ }
108
+
61
109
  /**
62
- * This function updates the _receipt to include `msg.value` and increments
110
+ * @notice This function updates the _receipt to include `msg.value` and increments
63
111
  * the number of tokens purchased by 1. It then checks that the updated
64
112
  * receipt is valid (i.e. sufficient funds have been posted for the
65
113
  * number of tokens purchased on the updated receipt), and reverts if not.
66
114
  * The new receipt net posted and num purchased are then returned to make
67
115
  * the values available in a gas-efficient manner to the caller of this
68
116
  * function.
69
- * @param _receipt Receipt struct for the user on the project being checked
70
- * @param _currentPriceInWei current price of token in Wei
117
+ * @param walletAddress Address of user to update receipt for
118
+ * @param projectId Project ID to update receipt for
119
+ * @param coreContract Core contract address
120
+ * @param currentPriceInWei current price of token in Wei
71
121
  * @return netPosted total funds posted by user on project (that have not
72
122
  * been yet settled), including the current transaction
73
123
  * @return numPurchased total number of tokens purchased by user on
74
124
  * project, including the current transaction
75
125
  */
76
- function validateReceiptEffects(
77
- Receipt storage _receipt,
78
- uint256 _currentPriceInWei
79
- ) internal returns (uint232 netPosted, uint24 numPurchased) {
126
+ function _validateReceiptEffects(
127
+ address walletAddress,
128
+ uint256 projectId,
129
+ address coreContract,
130
+ uint256 currentPriceInWei
131
+ ) private returns (uint232 netPosted, uint24 numPurchased) {
132
+ Receipt storage receipt = getReceipt({
133
+ walletAddress: walletAddress,
134
+ projectId: projectId,
135
+ coreContract: coreContract
136
+ });
80
137
  // in memory copy + update
81
- netPosted = (_receipt.netPosted + msg.value).toUint232();
82
- numPurchased = _receipt.numPurchased + 1;
138
+ netPosted = (receipt.netPosted + msg.value).toUint232();
139
+ numPurchased = receipt.numPurchased + 1;
83
140
 
84
141
  // require sufficient payment on project
85
142
  require(
86
- netPosted >= numPurchased * _currentPriceInWei,
143
+ netPosted >= numPurchased * currentPriceInWei,
87
144
  "Min value to mint req."
88
145
  );
89
146
 
90
147
  // update Receipt in storage
91
- // @dev overflow checks are not required since the added values cannot
92
- // be enough to overflow due to maximum invocations or supply of ETH
93
- _receipt.netPosted = netPosted;
94
- _receipt.numPurchased = numPurchased;
95
- }
96
-
97
- /**
98
- * @notice Emergency override function that allows a sellout price of a
99
- * completed auction to be reduced to something below current latest
100
- * purchase price, but gte base price. This is useful in the case that a
101
- * project's auction artifically sold out at a price higher than the
102
- * project's base price, and the artist wishes to allow settlement at a
103
- * lower price (and thus lower settlement revenues, increasing purchaser
104
- * returned settlement funds by the minter to collectors).
105
- * @param _projectId Project ID to reduce auction sellout price for.
106
- * @param _newSelloutPrice New sellout price to set for the auction. Must
107
- * be less than the current sellout price, gte base price.
108
- */
109
- function adminEmergencyReduceSelloutPrice(
110
- uint256 _projectId,
111
- address _coreContract,
112
- uint128 _newSelloutPrice,
113
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig,
114
- MaxInvocationsLib.MaxInvocationsProjectConfig
115
- storage _maxInvocationsProjectConfig,
116
- DAExpLib.DAProjectConfig storage _DAProjectConfig
117
- ) internal returns (bool maxInvocationsUpdated) {
118
- // CHECKS
119
- require(
120
- !_settlementAuctionProjectConfig.auctionRevenuesCollected,
121
- "Only before revenues collected"
122
- );
148
+ receipt.netPosted = netPosted;
149
+ receipt.numPurchased = numPurchased;
123
150
 
124
- // refresh max invocations, updating any local values that are
125
- // illogical with respect to the current core contract state, and
126
- // ensuring that local hasMaxBeenInvoked is accurate.
127
- maxInvocationsUpdated = MaxInvocationsLib.refreshMaxInvocations(
128
- _projectId,
129
- _coreContract,
130
- _maxInvocationsProjectConfig
131
- );
132
- require(
133
- _newSelloutPrice >= _DAProjectConfig.basePrice,
134
- "Only gte base price"
135
- );
136
- // require max invocations has been reached
137
- require(
138
- _maxInvocationsProjectConfig.maxHasBeenInvoked,
139
- "Auction must be complete"
140
- );
141
- // @dev no need to check that auction max invocations has been reached,
142
- // because if it was, the sellout price will be zero, and the following
143
- // check will fail.
144
- require(
145
- _newSelloutPrice <
146
- _settlementAuctionProjectConfig.latestPurchasePrice,
147
- "May only reduce sellout price"
148
- );
149
- // ensure _newSelloutPrice is non-zero
150
- // @dev this is a redundant check since minter doesn't allow base price
151
- // of zero, but is included in case that logic changes in the future.
152
- // @dev no coverage else branch of following line because redundant
153
- require(_newSelloutPrice > 0, "Only sellout prices > 0");
154
- // EFFECTS
155
- _settlementAuctionProjectConfig.latestPurchasePrice = _newSelloutPrice;
151
+ // emit event indicating new receipt state
152
+ emit ReceiptUpdated({
153
+ purchaser: msg.sender,
154
+ projectId: projectId,
155
+ coreContract: coreContract,
156
+ numPurchased: numPurchased,
157
+ netPosted: netPosted
158
+ });
156
159
  }
157
160
 
158
161
  /**
@@ -160,111 +163,410 @@ library SettlementExpLib {
160
163
  * marks the auction as having had its revenues collected.
161
164
  * IMPORTANT - this affects state and distributes revenue funds, so it
162
165
  * performs all three of CHECKS, EFFECTS and INTERACTIONS.
163
- * @param _projectId Project ID to get revenues for
164
- * @param _coreContract Core contract address
165
- * @param _settlementAuctionProjectConfig SettlementAuctionProjectConfig
166
- * struct for the project.
167
- * @param _maxInvocationsProjectConfig MaxInvocationProjectConfig struct
168
- * for the project.
169
- * @param _DAProjectConfig DAProjectConfig struct for the project.
170
- * @param _isEngine bool indicating whether the core contract is an engine
171
- * @return maxInvocationsUpdated whether or not the minter's local max
172
- * invocations state was updated during this function call.
166
+ * This function updates a project's balance to reflect the amount of
167
+ * revenues distributed, and will revert if underflow occurs.
168
+ * @param projectId Project ID to get revenues for
169
+ * @param coreContract Core contract address
173
170
  */
174
171
  function distributeArtistAndAdminRevenues(
175
- uint256 _projectId,
176
- address _coreContract,
177
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig,
178
- MaxInvocationsLib.MaxInvocationsProjectConfig
179
- storage _maxInvocationsProjectConfig,
180
- DAExpLib.DAProjectConfig storage _DAProjectConfig,
181
- bool _isEngine
182
- ) internal returns (bool maxInvocationsUpdated) {
172
+ uint256 projectId,
173
+ address coreContract
174
+ ) internal {
175
+ // load the project's settlement auction config
176
+ SettlementAuctionProjectConfig
177
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
178
+ projectId: projectId,
179
+ coreContract: coreContract
180
+ });
183
181
  // require revenues to not have already been collected
184
182
  require(
185
- !_settlementAuctionProjectConfig.auctionRevenuesCollected,
183
+ !settlementAuctionProjectConfig.auctionRevenuesCollected,
186
184
  "Revenues already collected"
187
185
  );
188
186
  // refresh max invocations, updating any local values that are
189
187
  // illogical with respect to the current core contract state, and
190
- // ensuring that local hasMaxBeenInvoked is accurate.
191
- maxInvocationsUpdated = MaxInvocationsLib.refreshMaxInvocations(
192
- _projectId,
193
- _coreContract,
194
- _maxInvocationsProjectConfig
195
- );
188
+ // ensuring that local maxHasBeenInvoked is accurate.
189
+ MaxInvocationsLib.refreshMaxInvocations({
190
+ projectId: projectId,
191
+ coreContract: coreContract
192
+ });
196
193
 
197
194
  // get the current net price of the auction - reverts if no auction
198
195
  // is configured.
199
196
  // @dev we use _getPriceUnsafe here, since we just safely synced the
200
197
  // project's max invocations and maxHasBeenInvoked, which guarantees
201
198
  // an accurate price calculation from _getPriceUnsafe, while being
202
- // more gas efficient than _getPriceSafe.
199
+ // more gas efficient than getPriceSafe.
203
200
  // @dev price is guaranteed <= _projectConfig.latestPurchasePrice,
204
201
  // since this minter enforces monotonically decreasing purchase prices.
205
- uint256 _price = getPriceUnsafe({
206
- _settlementAuctionProjectConfig: _settlementAuctionProjectConfig,
207
- _maxInvocationsProjectConfig: _maxInvocationsProjectConfig,
208
- _DAProjectConfig: _DAProjectConfig
202
+ // @dev we can trust maxHasBeenInvoked, since we just
203
+ // refreshed it above with refreshMaxInvocations, preventing any
204
+ // false negatives
205
+ bool maxHasBeenInvoked = MaxInvocationsLib.getMaxHasBeenInvoked({
206
+ projectId: projectId,
207
+ coreContract: coreContract
208
+ });
209
+ uint256 price = getPriceUnsafe({
210
+ projectId: projectId,
211
+ coreContract: coreContract,
212
+ maxHasBeenInvoked: maxHasBeenInvoked
209
213
  });
210
214
  // if the price is not base price, require that the auction have
211
215
  // reached max invocations. This prevents premature withdrawl
212
216
  // before final auction price is possible to know.
213
- if (_price != _DAProjectConfig.basePrice) {
214
- // @dev we can trust maxHasBeenInvoked, since we just
215
- // refreshed it above with refreshMaxInvocations, preventing any
216
- // false negatives
217
- require(
218
- _maxInvocationsProjectConfig.maxHasBeenInvoked,
219
- "Active auction not yet sold out"
220
- );
217
+ uint256 basePrice = DAExpLib.getAuctionBasePrice({
218
+ projectId: projectId,
219
+ coreContract: coreContract
220
+ });
221
+ if (price != basePrice) {
222
+ require(maxHasBeenInvoked, "Active auction not yet sold out");
223
+ // if max has been invoked, but all tokens to be auctioned were not
224
+ // sold, nonstandard activity has been detected (e.g. project max
225
+ // invocations were reduced on core contract after initial
226
+ // purchase, purchases were made on a different minter after
227
+ // initial purchase, etc.), which could artifically inflate
228
+ // sellout price and harm purchasers. In that case, we should
229
+ // not revert (since max invocations have been reached), but we
230
+ // should require admin to be the caller of this function.
231
+ // This provides separation of powers to protect collectors who
232
+ // participated in the auction.
233
+ // Note that if admin determines the artist has been malicious,
234
+ // admin should replace artist address with a wallet controlled by
235
+ // admin on the core contract, update payee address on the core,
236
+ // collect revenues using this function, then distribute any
237
+ // additional settlement funds to purchasers at admin's discretion.
238
+ if (
239
+ !_allTokensToBeAuctionedWereSold(settlementAuctionProjectConfig)
240
+ ) {
241
+ AuthLib.onlyCoreAdminACL({
242
+ coreContract: coreContract,
243
+ sender: msg.sender,
244
+ contract_: address(this),
245
+ selector: bytes4(
246
+ keccak256(
247
+ "distributeArtistAndAdminRevenues(uint256,address)"
248
+ )
249
+ )
250
+ });
251
+ }
221
252
  } else {
222
- uint128 basePrice = _DAProjectConfig.basePrice;
223
- // base price of zero indicates no sales, since base price of zero
224
- // is not allowed when configuring an auction.
253
+ // base price of zero indicates that the auction has not been configured,
254
+ // since base price of zero is not allowed when configuring an auction.
225
255
  // @dev no coverage else branch of following line because redundant
226
256
  require(basePrice > 0, "Only latestPurchasePrice > 0");
227
257
  // if the price is base price, the auction is valid and may be claimed
228
258
  // update the latest purchase price to the base price, to ensure
229
259
  // the base price is used for all future settlement calculations
230
260
  // EFFECTS
231
- _settlementAuctionProjectConfig.latestPurchasePrice = basePrice;
261
+ // @dev base price value was just loaded from uint88 in storage,
262
+ // so no safe cast required
263
+ settlementAuctionProjectConfig.latestPurchasePrice = uint88(
264
+ basePrice
265
+ );
266
+ // notify indexing service of settled price update
267
+ // @dev acknowledge that this event may be emitted prior to
268
+ // other state updates in this function, but that is okay because
269
+ // the settled price is the only value updated with this event
270
+ emit GenericMinterEventsLib.ConfigValueSet({
271
+ projectId: projectId,
272
+ coreContract: coreContract,
273
+ key: CONFIG_CURRENT_SETTLED_PRICE,
274
+ value: basePrice
275
+ });
232
276
  }
233
- _settlementAuctionProjectConfig.auctionRevenuesCollected = true;
277
+ settlementAuctionProjectConfig.auctionRevenuesCollected = true;
234
278
  // calculate the artist and admin revenues
235
- uint256 netRevenues = _settlementAuctionProjectConfig
236
- .numSettleableInvocations * _price;
279
+ uint256 netRevenues = settlementAuctionProjectConfig
280
+ .numSettleableInvocations * price;
281
+
282
+ // reduce project balance by the amount of ETH being distributed
283
+ // @dev underflow checked automatically in solidity ^0.8
284
+ settlementAuctionProjectConfig.projectBalance -= netRevenues.toUint88();
285
+
237
286
  // INTERACTIONS
238
- SplitFundsLib.splitRevenuesETH({
239
- _projectId: _projectId,
240
- _valueInWei: netRevenues,
241
- _coreContract: _coreContract,
242
- _isEngine: _isEngine
287
+ SplitFundsLib.splitRevenuesETHNoRefund({
288
+ projectId: projectId,
289
+ valueInWei: netRevenues,
290
+ coreContract: coreContract
291
+ });
292
+
293
+ emit GenericMinterEventsLib.ConfigValueSet({
294
+ projectId: projectId,
295
+ coreContract: coreContract,
296
+ key: CONFIG_AUCTION_REVENUES_COLLECTED,
297
+ value: true
298
+ });
299
+ }
300
+
301
+ /**
302
+ * @notice Reclaims excess settlement funds for purchaser wallet
303
+ * `purchaserAddress` on project `projectId`. Excess settlement funds are
304
+ * the amount of funds posted by the purchaser that are in excess of the
305
+ * amount required to settle the purchaser's tokens on the project.
306
+ * Excess settlement funds are sent to address `to`, and function reverts
307
+ * if send fails.
308
+ * @param projectId Project ID to reclaim excess settlement funds for
309
+ * @param coreContract Core contract address
310
+ * @param purchaserAddress Address to reclaim excess settlement funds for
311
+ * @param to Address to send excess settlement funds to
312
+ */
313
+ function reclaimProjectExcessSettlementFundsTo(
314
+ address payable to,
315
+ uint256 projectId,
316
+ address coreContract,
317
+ address purchaserAddress
318
+ ) internal {
319
+ (
320
+ uint256 excessSettlementFunds,
321
+ uint256 requiredAmountPosted
322
+ ) = getProjectExcessSettlementFunds({
323
+ projectId: projectId,
324
+ coreContract: coreContract,
325
+ walletAddress: purchaserAddress
326
+ });
327
+
328
+ SettlementAuctionProjectConfig
329
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
330
+ projectId: projectId,
331
+ coreContract: coreContract
332
+ });
333
+ Receipt storage receipt = getReceipt({
334
+ walletAddress: purchaserAddress,
335
+ coreContract: coreContract,
336
+ projectId: projectId
243
337
  });
244
- // @dev maxInvocationsUpdated is returned
338
+
339
+ uint232 newNetPosted = requiredAmountPosted.toUint232();
340
+ receipt.netPosted = newNetPosted;
341
+
342
+ // reduce project balance by the amount of ETH being distributed
343
+ // @dev underflow checked automatically in solidity ^0.8
344
+ settlementAuctionProjectConfig.projectBalance -= excessSettlementFunds
345
+ .toUint88();
346
+
347
+ emit ReceiptUpdated({
348
+ purchaser: purchaserAddress,
349
+ projectId: projectId,
350
+ coreContract: coreContract,
351
+ numPurchased: receipt.numPurchased,
352
+ netPosted: newNetPosted
353
+ });
354
+
355
+ // INTERACTIONS
356
+ bool success_;
357
+ (success_, ) = to.call{value: excessSettlementFunds}("");
358
+ require(success_, "Reclaiming failed");
245
359
  }
246
360
 
247
361
  /**
248
- *
249
- * @param _settlementAuctionProjectConfig SettlementAuctionProjectConfig
250
- * struct for the project.
251
- * Reverts if no purchases have been made on the project by the collector
252
- * being checked.
253
- * @param _receipt Receipt struct for the collector on the project being
254
- * checked
362
+ * @notice Performs updates to project state prior to a mint being
363
+ * initiated, during a purchase transaction. Specifically, this updates the
364
+ * number of purchases on this minter (and populates expected number of
365
+ * tokens to be auctioned if this is first purchase), increases the
366
+ * project's balance by the amount of funds sent with the transaction,
367
+ * updates the purchaser's receipt to reflect the new funds posted, checks
368
+ * that the updated receipt has sufficient funds posted for the number of
369
+ * tokens to be purchased after this transaction, and updates the project's
370
+ * latest purchase price to the current price of the token.
371
+ * Reverts if insuffient funds have been posted for the number of tokens to
372
+ * be purchased after this transaction.
373
+ * @param projectId Project ID to perform the pre-mint effects for
374
+ * @param coreContract Core contract address
375
+ * @param currentPriceInWei current price of token in Wei
376
+ * @param msgValue msg.value sent with mint transaction
377
+ * @param purchaserAddress Wallet address of purchaser
378
+ */
379
+ function preMintEffects(
380
+ uint256 projectId,
381
+ address coreContract,
382
+ uint256 currentPriceInWei,
383
+ uint256 msgValue,
384
+ address purchaserAddress
385
+ ) internal {
386
+ // load the project's settlement auction config and receipt
387
+ SettlementAuctionProjectConfig
388
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
389
+ projectId: projectId,
390
+ coreContract: coreContract
391
+ });
392
+
393
+ // if this is the first purchase on this minter, set the number of
394
+ // of tokens to be auctioned to:
395
+ // (minter max invocations) - (current core contract invocations)
396
+ if (settlementAuctionProjectConfig.numPurchasesOnMinter == 0) {
397
+ // get up-to-data invocation data from core contract
398
+ (
399
+ uint256 coreInvocations,
400
+ uint256 coreMaxInvocations
401
+ ) = MaxInvocationsLib.coreContractInvocationData({
402
+ projectId: projectId,
403
+ coreContract: coreContract
404
+ });
405
+ // snap chalkline on the number of tokens to be auctioned on this
406
+ // minter
407
+ // @dev acknowledge that this value may be stale if the core
408
+ // contract's max invocations were reduced since the last time
409
+ // the minter's max invocations were updated, but that is desired.
410
+ // That case would be classified as "nonstandard activity", so we
411
+ // want to require admin to be the withdrawer of revenues in that
412
+ // case.
413
+ uint256 minterMaxInvocations = MaxInvocationsLib.getMaxInvocations({
414
+ projectId: projectId,
415
+ coreContract: coreContract
416
+ });
417
+ // @dev prefer to use stale value minterMaxInvocations here, since
418
+ // if it is stale, the artist could have decreased max invocations
419
+ // on core contract at last moment unexpectedly, and we want to
420
+ // equire admin to be the withdrawer of revenues in that case.
421
+ settlementAuctionProjectConfig.numTokensToBeAuctioned = uint24(
422
+ minterMaxInvocations - coreInvocations
423
+ );
424
+ // edge case: minter max invocations > core contract max invocations.
425
+ // could be caused by either:
426
+ // - core contract max invocations reduced after configuring
427
+ // auction (this is generally accidental by artist), and did not
428
+ // subsequently update minter's max invocations
429
+ // - artist decreased max invocations on core contract at last
430
+ // moment prior to this initial mint (this is suspicious)
431
+ // either way, we want to update minter's local max invocations, so
432
+ // that the minter returns the most up-to-date value from function
433
+ // `projectMaxHasBeenInvoked` in the future.
434
+ // @dev note that this edge case will end up being classified as
435
+ // "nonstandard activity" in case of sellout above base price, and
436
+ // will require admin concurrence in those cases.
437
+ if (minterMaxInvocations > coreMaxInvocations) {
438
+ // update minter's max invocations to match core contract
439
+ MaxInvocationsLib.syncProjectMaxInvocationsToCore({
440
+ projectId: projectId,
441
+ coreContract: coreContract
442
+ });
443
+ }
444
+ }
445
+
446
+ // increment the number of purchases on this minter during every purchase
447
+ settlementAuctionProjectConfig.numPurchasesOnMinter++;
448
+
449
+ // update project balance
450
+ settlementAuctionProjectConfig.projectBalance += msgValue.toUint88();
451
+
452
+ _validateReceiptEffects({
453
+ walletAddress: purchaserAddress,
454
+ projectId: projectId,
455
+ coreContract: coreContract,
456
+ currentPriceInWei: currentPriceInWei
457
+ });
458
+
459
+ // update latest purchase price (on this minter) in storage
460
+ // @dev this is used to enforce monotonically decreasing purchase price
461
+ // across multiple auctions
462
+ settlementAuctionProjectConfig.latestPurchasePrice = currentPriceInWei
463
+ .toUint88();
464
+ }
465
+
466
+ /**
467
+ * @notice Performs updates to project state after a mint has been
468
+ * successfully completed.
469
+ * Specifically, this function distributes revenues if the auction revenues
470
+ * have been collected, or increments the number of settleable invocations
471
+ * if the auction revenues have not been collected.
472
+ * @param projectId Project ID to perform post-mint updates for
473
+ * @param coreContract Core contract address
474
+ * @param currentPriceInWei current price of token in Wei (the value to be
475
+ * distributed if revenues have been collected)
476
+ */
477
+ function postMintInteractions(
478
+ uint256 projectId,
479
+ address coreContract,
480
+ uint256 currentPriceInWei
481
+ ) internal {
482
+ // load the project's settlement auction config
483
+ SettlementAuctionProjectConfig
484
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
485
+ projectId: projectId,
486
+ coreContract: coreContract
487
+ });
488
+ if (settlementAuctionProjectConfig.auctionRevenuesCollected) {
489
+ // if revenues have been collected, split revenues immediately.
490
+ // @dev note that we are guaranteed to be at auction base price,
491
+ // since we know we didn't sellout prior to this tx.
492
+ // note that we don't refund msg.sender here, since a separate
493
+ // settlement mechanism is provided on this minter, unrelated to
494
+ // msg.value
495
+
496
+ // reduce project balance by the amount of ETH being distributed
497
+ // @dev specifically, this is not decremented by msg.value, as
498
+ // msg.sender is not refunded here
499
+ // @dev underflow checked automatically in solidity ^0.8
500
+ settlementAuctionProjectConfig.projectBalance -= currentPriceInWei
501
+ .toUint88();
502
+
503
+ // INTERACTIONS
504
+ SplitFundsLib.splitRevenuesETHNoRefund({
505
+ projectId: projectId,
506
+ valueInWei: currentPriceInWei,
507
+ coreContract: coreContract
508
+ });
509
+ } else {
510
+ // increment the number of settleable invocations that will be
511
+ // claimable by the artist and admin once auction is validated.
512
+ // do not split revenue here since will be claimed at a later time.
513
+ settlementAuctionProjectConfig.numSettleableInvocations++;
514
+ // @dev project balance is unaffected because no funds are distributed
515
+ }
516
+ }
517
+
518
+ /**
519
+ * Returns number of purchases that have been made on the minter, for a
520
+ * given project.
521
+ * @param projectId The id of the project.
522
+ * @param coreContract The address of the core contract.
523
+ */
524
+ function getNumPurchasesOnMinter(
525
+ uint256 projectId,
526
+ address coreContract
527
+ ) internal view returns (uint256) {
528
+ SettlementAuctionProjectConfig
529
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
530
+ projectId: projectId,
531
+ coreContract: coreContract
532
+ });
533
+ return settlementAuctionProjectConfig.numPurchasesOnMinter;
534
+ }
535
+
536
+ /**
537
+ * @notice Returns the excess settlement funds for purchaser wallet
538
+ * `walletAddress` on project `projectId`. Excess settlement funds are
539
+ * the amount of funds posted by the purchaser that are in excess of the
540
+ * amount required to settle the purchaser's tokens on the project.
541
+ * @param projectId Project ID to get revenues for
542
+ * @param coreContract Core contract address
543
+ * @param walletAddress Address to get excess settlement funds for
255
544
  * @return excessSettlementFunds excess settlement funds, in wei
256
545
  * @return requiredAmountPosted required amount to be posted by user, in wei
257
546
  */
258
547
  function getProjectExcessSettlementFunds(
259
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig,
260
- Receipt storage _receipt
548
+ uint256 projectId,
549
+ address coreContract,
550
+ address walletAddress
261
551
  )
262
552
  internal
263
553
  view
264
554
  returns (uint256 excessSettlementFunds, uint256 requiredAmountPosted)
265
555
  {
556
+ // load the project's settlement auction config
557
+ SettlementAuctionProjectConfig
558
+ storage _settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
559
+ projectId: projectId,
560
+ coreContract: coreContract
561
+ });
562
+ // load the user's receipt
563
+ Receipt storage receipt = getReceipt({
564
+ walletAddress: walletAddress,
565
+ projectId: projectId,
566
+ coreContract: coreContract
567
+ });
266
568
  // require that a user has purchased at least one token on this project
267
- uint256 numPurchased = _receipt.numPurchased;
569
+ uint256 numPurchased = receipt.numPurchased;
268
570
  require(numPurchased > 0, "No purchases made by this address");
269
571
 
270
572
  uint256 currentSettledTokenPrice = _settlementAuctionProjectConfig
@@ -273,12 +575,11 @@ library SettlementExpLib {
273
575
  // calculate the excess settlement funds amount
274
576
  // implicit overflow/underflow checks in solidity ^0.8
275
577
  requiredAmountPosted = numPurchased * currentSettledTokenPrice;
276
- excessSettlementFunds = _receipt.netPosted - requiredAmountPosted;
277
- return (excessSettlementFunds, requiredAmountPosted);
578
+ excessSettlementFunds = receipt.netPosted - requiredAmountPosted;
278
579
  }
279
580
 
280
581
  /**
281
- * @notice Gets price of minting a token on project `_projectId` given
582
+ * @notice Gets price of minting a token on project `projectId` given
282
583
  * the project's AuctionParameters and current block timestamp.
283
584
  * Reverts if auction has not yet started or auction is unconfigured, and
284
585
  * local hasMaxBeenInvoked is false and revenues have not been withdrawn.
@@ -286,105 +587,184 @@ library SettlementExpLib {
286
587
  * hasMaxBeenInvoked is stale and returning a false negative.
287
588
  * @dev when an accurate price is required regardless of the current state
288
589
  * state of the locally cached minter max invocations, use the less gas
289
- * efficient function `_getPriceSafe`.
290
- * @param _settlementAuctionProjectConfig SettlementAuctionProjectConfig
291
- * struct for the project.
292
- * @param _maxInvocationsProjectConfig MaxInvocationsProjectConfig
293
- * struct for the project.
294
- * @param _DAProjectConfig DAProjectConfig struct for the project.
590
+ * efficient function `getPriceSafe`.
591
+ * @param projectId Project ID to get price of token for.
592
+ * @param coreContract Core contract address to get price for.
593
+ * @param maxHasBeenInvoked Bool representing if maxHasBeenInvoked for the
594
+ * project.
295
595
  * @return uint256 current price of token in Wei, accurate if minter max
296
596
  * invocations are up to date
297
597
  * @dev This method calculates price decay using a linear interpolation
298
598
  * of exponential decay based on the artist-provided half-life for price
299
- * decay, `_priceDecayHalfLifeSeconds`.
599
+ * decay, `priceDecayHalfLifeSeconds`.
300
600
  */
301
601
  function getPriceUnsafe(
302
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig,
303
- MaxInvocationsLib.MaxInvocationsProjectConfig
304
- storage _maxInvocationsProjectConfig,
305
- DAExpLib.DAProjectConfig storage _DAProjectConfig
602
+ uint256 projectId,
603
+ address coreContract,
604
+ bool maxHasBeenInvoked
306
605
  ) internal view returns (uint256) {
606
+ // load the project's settlement auction config
607
+ SettlementAuctionProjectConfig
608
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
609
+ projectId: projectId,
610
+ coreContract: coreContract
611
+ });
307
612
  // return latest purchase price if:
308
613
  // - minter is aware of a sold-out auction (without updating max
309
614
  // invocation value)
310
615
  // - auction revenues have been collected, at which point the latest
311
616
  // purchase price will never change again
312
617
  if (
313
- _maxInvocationsProjectConfig.maxHasBeenInvoked ||
314
- _settlementAuctionProjectConfig.auctionRevenuesCollected
618
+ maxHasBeenInvoked ||
619
+ settlementAuctionProjectConfig.auctionRevenuesCollected
315
620
  ) {
316
- return _settlementAuctionProjectConfig.latestPurchasePrice;
621
+ return settlementAuctionProjectConfig.latestPurchasePrice;
317
622
  }
318
623
  // otherwise calculate price based on current block timestamp and
319
624
  // auction configuration
320
625
  // @dev this will revert if auction has not yet started or auction is
321
626
  // unconfigured, which is relied upon for security.
322
- return DAExpLib.getPriceExp({_DAProjectConfig: _DAProjectConfig});
627
+ return
628
+ DAExpLib.getPriceExp({
629
+ projectId: projectId,
630
+ coreContract: coreContract
631
+ });
323
632
  }
324
633
 
325
634
  /**
326
- * @notice Gets price of minting a token on project `_projectId` given
635
+ * @notice Gets price of minting a token on project `projectId` given
327
636
  * the project's AuctionParameters and current block timestamp.
328
637
  * This is labeled as "safe", because price is guaranteed to be accurate
329
638
  * even in the case of a stale locally cached minter max invocations.
330
639
  * Reverts if auction has not yet started or auction is unconfigured, and
331
640
  * auction has not sold out or revenues have not been withdrawn.
332
- * @dev This method is less gas efficient than `_getPriceUnsafe`, but is
641
+ * @dev This method is less gas efficient than `getPriceUnsafe`, but is
333
642
  * guaranteed to be accurate.
334
- * @param _projectId Project ID to get price of token for.
643
+ * @param projectId Project ID to get price of token for.
644
+ * @param coreContract Core contract address to get price for.
335
645
  * @return tokenPriceInWei current price of token in Wei
336
646
  * @dev This method calculates price decay using a linear interpolation
337
647
  * of exponential decay based on the artist-provided half-life for price
338
- * decay, `_priceDecayHalfLifeSeconds`.
648
+ * decay, `priceDecayHalfLifeSeconds`.
339
649
  */
340
650
  function getPriceSafe(
341
- uint256 _projectId,
342
- address _coreContract,
343
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig,
344
- MaxInvocationsLib.MaxInvocationsProjectConfig
345
- storage _maxInvocationsProjectConfig,
346
- DAExpLib.DAProjectConfig storage _DAProjectConfig
651
+ uint256 projectId,
652
+ address coreContract
347
653
  ) internal view returns (uint256 tokenPriceInWei) {
654
+ // load the project's settlement auction config
655
+ SettlementAuctionProjectConfig
656
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
657
+ projectId: projectId,
658
+ coreContract: coreContract
659
+ });
348
660
  // accurately check if project has sold out
349
661
  if (
350
662
  MaxInvocationsLib.projectMaxHasBeenInvokedSafe({
351
- _projectId: _projectId,
352
- _coreContract: _coreContract,
353
- _maxInvocationsProjectConfig: _maxInvocationsProjectConfig
663
+ projectId: projectId,
664
+ coreContract: coreContract
354
665
  })
355
666
  ) {
356
667
  // max invocations have been reached, return the latest purchased
357
668
  // price
358
- tokenPriceInWei = _settlementAuctionProjectConfig
669
+ tokenPriceInWei = settlementAuctionProjectConfig
359
670
  .latestPurchasePrice;
360
671
  } else {
361
672
  // if not sold out, return the current price via getPriceUnsafe
362
673
  tokenPriceInWei = getPriceUnsafe({
363
- _settlementAuctionProjectConfig: _settlementAuctionProjectConfig,
364
- _maxInvocationsProjectConfig: _maxInvocationsProjectConfig,
365
- _DAProjectConfig: _DAProjectConfig
674
+ projectId: projectId,
675
+ coreContract: coreContract,
676
+ maxHasBeenInvoked: false // this branch is only reached if max invocations have not been reached
366
677
  });
367
678
  }
368
679
  return tokenPriceInWei;
369
680
  }
370
681
 
371
682
  /**
372
- * Returns if a new auction's start price is valid, given the current
683
+ * @notice Returns if a new auction's start price is valid, given the current
373
684
  * state of the project's settlement auction configuration.
374
- * @param _startPrice starting price of new auction, in wei
375
- * @param _settlementAuctionProjectConfig SettlementAuctionProjectConfig
376
- * struct for the project being configured for a new auction.
685
+ * @param projectId Project ID to check start price for
686
+ * @param coreContract Core contract address to check start price for
687
+ * @param startPrice starting price of new auction, in wei
377
688
  */
378
689
  function isValidStartPrice(
379
- uint256 _startPrice,
380
- SettlementAuctionProjectConfig storage _settlementAuctionProjectConfig
690
+ uint256 projectId,
691
+ address coreContract,
692
+ uint256 startPrice
381
693
  ) internal view returns (bool) {
694
+ // load the project's settlement auction config
695
+ SettlementAuctionProjectConfig
696
+ storage settlementAuctionProjectConfig = getSettlementAuctionProjectConfig({
697
+ projectId: projectId,
698
+ coreContract: coreContract
699
+ });
382
700
  // If previous purchases have been made, require monotonically
383
701
  // decreasing purchase prices to preserve settlement and revenue
384
702
  // claiming logic. Since base price is always non-zero, if
385
703
  // latestPurchasePrice is zero, then no previous purchases have been
386
704
  // made, and startPrice may be set to any value.
387
- return (_settlementAuctionProjectConfig.latestPurchasePrice == 0 || // never purchased
388
- _startPrice <= _settlementAuctionProjectConfig.latestPurchasePrice);
705
+ return (settlementAuctionProjectConfig.latestPurchasePrice == 0 || // never purchased
706
+ startPrice <= settlementAuctionProjectConfig.latestPurchasePrice);
707
+ }
708
+
709
+ /**
710
+ * Loads the SettlementAuctionProjectConfig for a given project and core
711
+ * contract.
712
+ * @param projectId Project Id to get config for
713
+ * @param coreContract Core contract address to get config for
714
+ */
715
+ function getSettlementAuctionProjectConfig(
716
+ uint256 projectId,
717
+ address coreContract
718
+ ) internal view returns (SettlementAuctionProjectConfig storage) {
719
+ return s().settlementAuctionProjectConfigs[coreContract][projectId];
720
+ }
721
+
722
+ /**
723
+ * Loads the Receipt for a given user, project and core contract.
724
+ * @param walletAddress User address to get receipt for
725
+ * @param projectId Project Id to get config for
726
+ * @param coreContract Core contract address to get config for
727
+ */
728
+ function getReceipt(
729
+ address walletAddress,
730
+ uint256 projectId,
731
+ address coreContract
732
+ ) internal view returns (Receipt storage) {
733
+ return s().receipts[walletAddress][coreContract][projectId];
734
+ }
735
+
736
+ /**
737
+ * @notice Return the storage struct for reading and writing. This library
738
+ * uses a diamond storage pattern when managing storage.
739
+ * @return storageStruct The SettlementExpLibStorage struct.
740
+ */
741
+ function s()
742
+ internal
743
+ pure
744
+ returns (SettlementExpLibStorage storage storageStruct)
745
+ {
746
+ bytes32 position = SETTLEMENT_EXP_LIB_STORAGE_POSITION;
747
+ assembly ("memory-safe") {
748
+ storageStruct.slot := position
749
+ }
750
+ }
751
+
752
+ /**
753
+ * Returns if all tokens to be auctioned were sold, for a given project.
754
+ * Returns false if the number of tokens to be auctioned is zero, since
755
+ * that is the default value for unconfigured values.
756
+ * @param settlementAuctionProjectConfig The SettlementAuctionProjectConfig
757
+ * struct of the project to check.
758
+ */
759
+ function _allTokensToBeAuctionedWereSold(
760
+ SettlementAuctionProjectConfig storage settlementAuctionProjectConfig
761
+ ) private view returns (bool) {
762
+ // @dev load numAllocations into memory for gas efficiency
763
+ uint256 numTokensToBeAuctioned = settlementAuctionProjectConfig
764
+ .numTokensToBeAuctioned;
765
+ return
766
+ numTokensToBeAuctioned > 0 &&
767
+ settlementAuctionProjectConfig.numPurchasesOnMinter ==
768
+ numTokensToBeAuctioned;
389
769
  }
390
770
  }