@artblocks/contracts 0.0.1 → 1.0.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 (113) hide show
  1. package/README.md +27 -2
  2. package/artifacts/contracts/archive/GenArt721CoreV0.sol/Address.json +10 -0
  3. package/artifacts/contracts/archive/GenArt721CoreV0.sol/Counters.json +10 -0
  4. package/artifacts/contracts/archive/GenArt721CoreV0.sol/CustomERC721Metadata.json +425 -0
  5. package/artifacts/contracts/archive/GenArt721CoreV0.sol/ERC165.json +38 -0
  6. package/artifacts/contracts/archive/GenArt721CoreV0.sol/ERC721.json +322 -0
  7. package/artifacts/contracts/archive/GenArt721CoreV0.sol/ERC721Enumerable.json +384 -0
  8. package/artifacts/contracts/archive/GenArt721CoreV0.sol/ERC721ReceiverMock.json +101 -0
  9. package/artifacts/contracts/archive/GenArt721CoreV0.sol/GenArt721.json +1528 -0
  10. package/artifacts/contracts/archive/GenArt721CoreV0.sol/IERC165.json +32 -0
  11. package/artifacts/contracts/archive/GenArt721CoreV0.sol/IERC721.json +316 -0
  12. package/artifacts/contracts/archive/GenArt721CoreV0.sol/IERC721Enumerable.json +378 -0
  13. package/artifacts/contracts/archive/GenArt721CoreV0.sol/IERC721Receiver.json +47 -0
  14. package/artifacts/contracts/archive/GenArt721CoreV0.sol/SafeMath.json +10 -0
  15. package/artifacts/contracts/archive/GenArt721CoreV0.sol/Strings.json +10 -0
  16. package/artifacts/contracts/archive/GenArt721CoreV1.sol/Address.json +10 -0
  17. package/artifacts/contracts/archive/GenArt721CoreV1.sol/Counters.json +10 -0
  18. package/artifacts/contracts/archive/GenArt721CoreV1.sol/CustomERC721Metadata.json +425 -0
  19. package/artifacts/contracts/archive/GenArt721CoreV1.sol/ERC165.json +38 -0
  20. package/artifacts/contracts/archive/GenArt721CoreV1.sol/ERC721.json +322 -0
  21. package/artifacts/contracts/archive/GenArt721CoreV1.sol/ERC721Enumerable.json +384 -0
  22. package/artifacts/contracts/archive/GenArt721CoreV1.sol/GenArt721Core.json +1773 -0
  23. package/artifacts/contracts/archive/GenArt721CoreV1.sol/IERC165.json +32 -0
  24. package/artifacts/contracts/archive/GenArt721CoreV1.sol/IERC721.json +316 -0
  25. package/artifacts/contracts/archive/GenArt721CoreV1.sol/IERC721Enumerable.json +378 -0
  26. package/artifacts/contracts/archive/GenArt721CoreV1.sol/IERC721Receiver.json +47 -0
  27. package/artifacts/contracts/archive/GenArt721CoreV1.sol/Randomizer.json +26 -0
  28. package/artifacts/contracts/archive/GenArt721CoreV1.sol/SafeMath.json +10 -0
  29. package/artifacts/contracts/archive/GenArt721CoreV1.sol/Strings.json +10 -0
  30. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV0.sol/MinterDAExpV0.json +575 -0
  31. package/artifacts/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV1.sol/MinterDAExpV1.json +463 -0
  32. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExp/MinterDAExpV2.sol/MinterDAExpV2.json +3 -3
  33. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExp/MinterDAExpV3.sol/MinterDAExpV3.json +3 -3
  34. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol/MinterDAExpSettlementV0.json +15 -15
  35. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV0.sol/MinterDALinV0.json +551 -0
  36. package/artifacts/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV1.sol/MinterDALinV1.json +439 -0
  37. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDALin/MinterDALinV2.sol/MinterDALinV2.json +3 -3
  38. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDALin/MinterDALinV3.sol/MinterDALinV3.json +3 -3
  39. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV0.sol/MinterHolderV0.json +622 -0
  40. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV1.sol/MinterHolderV1.json +719 -0
  41. package/artifacts/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV2.sol/MinterHolderV2.json +794 -0
  42. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterHolder/MinterHolderV3.sol/MinterHolderV3.json +3 -3
  43. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV0.sol/MinterMerkleV0.json +873 -0
  44. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol/MinterMerkleV1.json +932 -0
  45. package/artifacts/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV2.sol/MinterMerkleV2.json +984 -0
  46. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterMerkle/MinterMerkleV3.sol/MinterMerkleV3.json +3 -3
  47. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterMerkle/MinterMerkleV4.sol/MinterMerkleV4.json +3 -3
  48. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV0.sol/MinterSetPriceV0.json +400 -0
  49. package/artifacts/contracts/{minter-suite/Minters/MinterSetPrice/deprecated → archive/minter-suite/Minters/MinterSetPrice}/MinterSetPriceV1.sol/MinterSetPriceV1.json +3 -3
  50. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPrice/MinterSetPriceV2.sol/MinterSetPriceV2.json +3 -3
  51. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPrice/MinterSetPriceV3.sol/MinterSetPriceV3.json +3 -3
  52. package/artifacts/contracts/{minter-suite/Minters/MinterSetPriceERC20/deprecated → archive/minter-suite/Minters/MinterSetPriceERC20}/MinterSetPriceERC20V0.sol/MinterSetPriceERC20V0.json +3 -3
  53. package/artifacts/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V1.sol/MinterSetPriceERC20V1.json +349 -0
  54. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPriceERC20/MinterSetPriceERC20V2.sol/MinterSetPriceERC20V2.json +3 -3
  55. package/artifacts/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPriceERC20/MinterSetPriceERC20V3.sol/MinterSetPriceERC20V3.json +3 -3
  56. package/artifacts/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlementV0.sol/IFilteredMinterDAExpSettlementV0.json +12 -12
  57. package/artifacts/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlement_Mixin.sol/IFilteredMinterDAExpSettlement_Mixin.json +161 -0
  58. package/artifacts/contracts/interfaces/0.8.x/IMinterBaseV0.sol/IMinterBaseV0.json +24 -0
  59. package/artifacts/contracts/interfaces/0.8.x/IMintingAPICoreV0.sol/IMintingAPICoreV0.json +185 -0
  60. package/artifacts/contracts/interfaces/0.8.x/IMintingAPIMinterV0.sol/IMintingAPIMinterV0.json +88 -0
  61. package/artifacts/contracts/minter-suite/Minters/MinterBase_v0_1_1.sol/MinterBase.json +24 -0
  62. package/artifacts/contracts/minter-suite/Minters/MinterDAExpSettlementV1.sol/MinterDAExpSettlementV1.json +1066 -0
  63. package/artifacts/contracts/minter-suite/Minters/MinterDAExpV4.sol/MinterDAExpV4.json +869 -0
  64. package/artifacts/contracts/minter-suite/Minters/MinterDALinV4.sol/MinterDALinV4.json +845 -0
  65. package/artifacts/contracts/minter-suite/Minters/MinterHolderV4.sol/MinterHolderV4.json +1113 -0
  66. package/artifacts/contracts/minter-suite/Minters/MinterMerkleV5.sol/MinterMerkleV5.json +1052 -0
  67. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceERC20V4.sol/MinterSetPriceERC20V4.json +755 -0
  68. package/artifacts/contracts/minter-suite/Minters/MinterSetPriceV4.sol/MinterSetPriceV4.json +684 -0
  69. package/artifacts/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol/GenArt721CoreV3_Engine_IncorrectCoreType.json +1817 -0
  70. package/contracts/archive/GenArt721CoreV0.sol +1621 -0
  71. package/contracts/archive/GenArt721CoreV1.sol +1577 -0
  72. package/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV0.sol +503 -0
  73. package/contracts/archive/minter-suite/Minters/MinterDAExp/MinterDAExpV1.sol +442 -0
  74. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExp/MinterDAExpV2.sol +3 -3
  75. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExp/MinterDAExpV3.sol +3 -3
  76. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDAExpSettlement/MinterDAExpSettlementV0.sol +3 -3
  77. package/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV0.sol +450 -0
  78. package/contracts/archive/minter-suite/Minters/MinterDALin/MinterDALinV1.sol +406 -0
  79. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDALin/MinterDALinV2.sol +3 -3
  80. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterDALin/MinterDALinV3.sol +3 -3
  81. package/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV0.sol +540 -0
  82. package/contracts/archive/minter-suite/Minters/MinterHolder/MinterHolderV1.sol +648 -0
  83. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterHolder/MinterHolderV2.sol +6 -6
  84. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterHolder/MinterHolderV3.sol +4 -4
  85. package/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV0.sol +523 -0
  86. package/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV1.sol +602 -0
  87. package/contracts/archive/minter-suite/Minters/MinterMerkle/MinterMerkleV2.sol +679 -0
  88. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterMerkle/MinterMerkleV3.sol +4 -4
  89. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterMerkle/MinterMerkleV4.sol +4 -4
  90. package/contracts/archive/minter-suite/Minters/MinterSetPrice/MinterSetPriceV0.sol +333 -0
  91. package/contracts/{minter-suite/Minters/MinterSetPrice/deprecated → archive/minter-suite/Minters/MinterSetPrice}/MinterSetPriceV1.sol +0 -0
  92. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPrice/MinterSetPriceV2.sol +3 -3
  93. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPrice/MinterSetPriceV3.sol +3 -3
  94. package/contracts/{minter-suite/Minters/MinterSetPriceERC20/deprecated → archive/minter-suite/Minters/MinterSetPriceERC20}/MinterSetPriceERC20V0.sol +0 -0
  95. package/contracts/archive/minter-suite/Minters/MinterSetPriceERC20/MinterSetPriceERC20V1.sol +411 -0
  96. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPriceERC20/MinterSetPriceERC20V2.sol +3 -3
  97. package/contracts/{minter-suite → archive/minter-suite}/Minters/MinterSetPriceERC20/MinterSetPriceERC20V3.sol +3 -3
  98. package/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlementV0.sol +4 -53
  99. package/contracts/interfaces/0.8.x/IFilteredMinterDAExpSettlement_Mixin.sol +62 -0
  100. package/contracts/interfaces/0.8.x/IMinterBaseV0.sol +20 -0
  101. package/contracts/interfaces/0.8.x/IMintingAPICoreV0.sol +62 -0
  102. package/contracts/interfaces/0.8.x/IMintingAPIMinterV0.sol +31 -0
  103. package/contracts/minter-suite/Minters/MinterBase_v0_1_1.sol +293 -0
  104. package/contracts/minter-suite/Minters/MinterDAExpSettlementV1.sol +1026 -0
  105. package/contracts/minter-suite/Minters/MinterDAExpV4.sol +572 -0
  106. package/contracts/minter-suite/Minters/MinterDALinV4.sol +525 -0
  107. package/contracts/minter-suite/Minters/MinterHolderV4.sol +752 -0
  108. package/contracts/minter-suite/Minters/MinterMerkleV5.sol +688 -0
  109. package/contracts/minter-suite/Minters/MinterSetPriceERC20V4.sol +451 -0
  110. package/contracts/minter-suite/Minters/MinterSetPriceV4.sol +352 -0
  111. package/contracts/mock/GenArt721CoreV3_Engine_IncorrectCoreType.sol +1979 -0
  112. package/package.json +1 -1
  113. package/artifacts/contracts/minter-suite/Minters/MinterHolder/MinterHolderV2.sol/MinterHolderV2.json +0 -794
@@ -0,0 +1,688 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ // Created By: Art Blocks Inc.
3
+
4
+ import "../../interfaces/0.8.x/IGenArt721CoreContractV3_Base.sol";
5
+ import "../../interfaces/0.8.x/IMinterFilterV0.sol";
6
+ import "../../interfaces/0.8.x/IFilteredMinterMerkleV2.sol";
7
+ import "../../interfaces/0.8.x/IDelegationRegistry.sol";
8
+ import "./MinterBase_v0_1_1.sol";
9
+
10
+ import "@openzeppelin-4.7/contracts/utils/cryptography/MerkleProof.sol";
11
+ import "@openzeppelin-4.7/contracts/token/ERC20/IERC20.sol";
12
+ import "@openzeppelin-4.7/contracts/security/ReentrancyGuard.sol";
13
+
14
+ pragma solidity 0.8.17;
15
+
16
+ /**
17
+ * @title Filtered Minter contract that allows tokens to be minted with ETH
18
+ * for addresses in a Merkle allowlist.
19
+ * This is designed to be used with GenArt721CoreContractV3 flagship or
20
+ * engine contracts.
21
+ * @author Art Blocks Inc.
22
+ * @notice Privileged Roles and Ownership:
23
+ * This contract is designed to be managed, with limited powers.
24
+ * Privileged roles and abilities are controlled by the project's artist, which
25
+ * can be modified by the core contract's Admin ACL contract. Both of these
26
+ * roles hold extensive power and can modify minter details.
27
+ * Care must be taken to ensure that the admin ACL contract and artist
28
+ * addresses are secure behind a multi-sig or other access control mechanism.
29
+ * ----------------------------------------------------------------------------
30
+ * The following functions are restricted to a project's artist:
31
+ * - updateMerkleRoot
32
+ * - updatePricePerTokenInWei
33
+ * - setProjectInvocationsPerAddress
34
+ * - setProjectMaxInvocations
35
+ * - manuallyLimitProjectMaxInvocations
36
+ * ----------------------------------------------------------------------------
37
+ * Additional admin and artist privileged roles may be described on other
38
+ * contracts that this minter integrates with.
39
+ * ----------------------------------------------------------------------------
40
+ * This contract allows vaults to configure token-level or wallet-level
41
+ * delegation of minting privileges. This allows a vault on an allowlist to
42
+ * delegate minting privileges to a wallet that is not on the allowlist,
43
+ * enabling the vault to remain air-gapped while still allowing minting. The
44
+ * delegation registry contract is responsible for managing these delegations,
45
+ * and is available at the address returned by the public immutable
46
+ * `delegationRegistryAddress`. At the time of writing, the delegation
47
+ * registry enables easy delegation configuring at https://delegate.cash/.
48
+ * Art Blocks does not guarentee the security of the delegation registry, and
49
+ * users should take care to ensure that the delegation registry is secure.
50
+ * Token-level delegations are configured by the vault owner, and contract-
51
+ * level delegations must be configured for the core token contract as returned
52
+ * by the public immutable variable `genArt721CoreAddress`.
53
+ */
54
+ contract MinterMerkleV5 is
55
+ ReentrancyGuard,
56
+ MinterBase,
57
+ IFilteredMinterMerkleV2
58
+ {
59
+ using MerkleProof for bytes32[];
60
+
61
+ /// Delegation registry address
62
+ address public immutable delegationRegistryAddress;
63
+
64
+ /// Delegation registry address
65
+ IDelegationRegistry private immutable delegationRegistryContract;
66
+
67
+ /// Core contract address this minter interacts with
68
+ address public immutable genArt721CoreAddress;
69
+
70
+ /// The core contract integrates with V3 contracts
71
+ IGenArt721CoreContractV3_Base private immutable genArtCoreContract_Base;
72
+
73
+ /// Minter filter address this minter interacts with
74
+ address public immutable minterFilterAddress;
75
+
76
+ /// Minter filter this minter may interact with.
77
+ IMinterFilterV0 private immutable minterFilter;
78
+
79
+ /// minterType for this minter
80
+ string public constant minterType = "MinterMerkleV5";
81
+
82
+ /// project minter configuration keys used by this minter
83
+ bytes32 private constant CONFIG_MERKLE_ROOT = "merkleRoot";
84
+ bytes32 private constant CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE =
85
+ "useMaxMintsPerAddrOverride"; // shortened to fit in 32 bytes
86
+ bytes32 private constant CONFIG_MAX_INVOCATIONS_OVERRIDE =
87
+ "maxMintsPerAddrOverride"; // shortened to match format of previous key
88
+
89
+ uint256 constant ONE_MILLION = 1_000_000;
90
+
91
+ uint256 public constant DEFAULT_MAX_INVOCATIONS_PER_ADDRESS = 1;
92
+
93
+ struct ProjectConfig {
94
+ bool maxHasBeenInvoked;
95
+ bool priceIsConfigured;
96
+ // initial value is false, so by default, projects limit allowlisted
97
+ // addresses to a mint qty of `DEFAULT_MAX_INVOCATIONS_PER_ADDRESS`
98
+ bool useMaxInvocationsPerAddressOverride;
99
+ // a value of 0 means no limit
100
+ // (only used if `useMaxInvocationsPerAddressOverride` is true)
101
+ uint24 maxInvocationsPerAddressOverride;
102
+ uint24 maxInvocations;
103
+ uint256 pricePerTokenInWei;
104
+ }
105
+
106
+ mapping(uint256 => ProjectConfig) public projectConfig;
107
+
108
+ /// projectId => merkle root
109
+ mapping(uint256 => bytes32) public projectMerkleRoot;
110
+
111
+ /// projectId => purchaser address => qty of mints purchased for project
112
+ mapping(uint256 => mapping(address => uint256))
113
+ public projectUserMintInvocations;
114
+
115
+ modifier onlyArtist(uint256 _projectId) {
116
+ require(
117
+ msg.sender ==
118
+ genArtCoreContract_Base.projectIdToArtistAddress(_projectId),
119
+ "Only Artist"
120
+ );
121
+ _;
122
+ }
123
+
124
+ /**
125
+ * @notice Initializes contract to be a Filtered Minter for
126
+ * `_minterFilter`, integrated with Art Blocks core contract
127
+ * at address `_genArt721Address`. Also configures the delegation
128
+ * registry address to be address `_delegationRegistryAddress`.
129
+ * @param _genArt721Address Art Blocks core contract address for
130
+ * which this contract will be a minter.
131
+ * @param _minterFilter Minter filter for which this will be a
132
+ * filtered minter.
133
+ * @param _delegationRegistryAddress Delegation registry contract address.
134
+ */
135
+ constructor(
136
+ address _genArt721Address,
137
+ address _minterFilter,
138
+ address _delegationRegistryAddress
139
+ ) ReentrancyGuard() MinterBase(_genArt721Address) {
140
+ genArt721CoreAddress = _genArt721Address;
141
+ // always populate immutable engine contracts, but only use appropriate
142
+ // interface based on isEngine in the rest of the contract
143
+ genArtCoreContract_Base = IGenArt721CoreContractV3_Base(
144
+ _genArt721Address
145
+ );
146
+ delegationRegistryAddress = _delegationRegistryAddress;
147
+ emit DelegationRegistryUpdated(_delegationRegistryAddress);
148
+ delegationRegistryContract = IDelegationRegistry(
149
+ _delegationRegistryAddress
150
+ );
151
+ minterFilterAddress = _minterFilter;
152
+ minterFilter = IMinterFilterV0(_minterFilter);
153
+ require(
154
+ minterFilter.genArt721CoreAddress() == _genArt721Address,
155
+ "Illegal contract pairing"
156
+ );
157
+ // broadcast default max invocations per address for this minter
158
+ emit DefaultMaxInvocationsPerAddress(
159
+ DEFAULT_MAX_INVOCATIONS_PER_ADDRESS
160
+ );
161
+ }
162
+
163
+ /**
164
+ * @notice Update the Merkle root for project `_projectId`.
165
+ * @param _projectId Project ID to be updated.
166
+ * @param _root root of Merkle tree defining addresses allowed to mint
167
+ * on project `_projectId`.
168
+ */
169
+ function updateMerkleRoot(
170
+ uint256 _projectId,
171
+ bytes32 _root
172
+ ) external onlyArtist(_projectId) {
173
+ require(_root != bytes32(0), "Root must be provided");
174
+ projectMerkleRoot[_projectId] = _root;
175
+ emit ConfigValueSet(_projectId, CONFIG_MERKLE_ROOT, _root);
176
+ }
177
+
178
+ /**
179
+ * @notice Returns hashed address (to be used as merkle tree leaf).
180
+ * Included as a public function to enable users to calculate their hashed
181
+ * address in Solidity when generating proofs off-chain.
182
+ * @param _address address to be hashed
183
+ * @return bytes32 hashed address, via keccak256 (using encodePacked)
184
+ */
185
+ function hashAddress(address _address) public pure returns (bytes32) {
186
+ return keccak256(abi.encodePacked(_address));
187
+ }
188
+
189
+ /**
190
+ * @notice Verify if address is allowed to mint on project `_projectId`.
191
+ * @param _projectId Project ID to be checked.
192
+ * @param _proof Merkle proof for address.
193
+ * @param _address Address to check.
194
+ * @return inAllowlist true only if address is allowed to mint and valid
195
+ * Merkle proof was provided
196
+ */
197
+ function verifyAddress(
198
+ uint256 _projectId,
199
+ bytes32[] calldata _proof,
200
+ address _address
201
+ ) public view returns (bool) {
202
+ return
203
+ _proof.verifyCalldata(
204
+ projectMerkleRoot[_projectId],
205
+ hashAddress(_address)
206
+ );
207
+ }
208
+
209
+ /**
210
+ * @notice Sets maximum allowed invocations per allowlisted address for
211
+ * project `_project` to `limit`. If `limit` is set to 0, allowlisted
212
+ * addresses will be able to mint as many times as desired, until the
213
+ * project reaches its maximum invocations.
214
+ * Default is a value of 1 if never configured by artist.
215
+ * @param _projectId Project ID to toggle the mint limit.
216
+ * @param _maxInvocationsPerAddress Maximum allowed invocations per
217
+ * allowlisted address.
218
+ * @dev default value stated above must be updated if the value of
219
+ * CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
220
+ */
221
+ function setProjectInvocationsPerAddress(
222
+ uint256 _projectId,
223
+ uint24 _maxInvocationsPerAddress
224
+ ) external onlyArtist(_projectId) {
225
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
226
+ // use override value instead of the contract's default
227
+ // @dev this never changes from true to false; default value is only
228
+ // used if artist has never configured project invocations per address
229
+ _projectConfig.useMaxInvocationsPerAddressOverride = true;
230
+ // update the override value
231
+ _projectConfig
232
+ .maxInvocationsPerAddressOverride = _maxInvocationsPerAddress;
233
+ // generic events
234
+ emit ConfigValueSet(
235
+ _projectId,
236
+ CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE,
237
+ true
238
+ );
239
+ emit ConfigValueSet(
240
+ _projectId,
241
+ CONFIG_MAX_INVOCATIONS_OVERRIDE,
242
+ uint256(_maxInvocationsPerAddress)
243
+ );
244
+ }
245
+
246
+ /**
247
+ * @notice Syncs local maximum invocations of project `_projectId` based on
248
+ * the value currently defined in the core contract.
249
+ * @param _projectId Project ID to set the maximum invocations for.
250
+ * @dev this enables gas reduction after maxInvocations have been reached -
251
+ * core contracts shall still enforce a maxInvocation check during mint.
252
+ */
253
+ function setProjectMaxInvocations(
254
+ uint256 _projectId
255
+ ) external onlyArtist(_projectId) {
256
+ uint256 maxInvocations;
257
+ uint256 invocations;
258
+ (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
259
+ .projectStateData(_projectId);
260
+ // update storage with results
261
+ projectConfig[_projectId].maxInvocations = uint24(maxInvocations);
262
+
263
+ // We need to ensure maxHasBeenInvoked is correctly set after manually syncing the
264
+ // local maxInvocations value with the core contract's maxInvocations value.
265
+ // This synced value of maxInvocations from the core contract will always be greater
266
+ // than or equal to the previous value of maxInvocations stored locally.
267
+ projectConfig[_projectId].maxHasBeenInvoked =
268
+ invocations == maxInvocations;
269
+
270
+ emit ProjectMaxInvocationsLimitUpdated(_projectId, maxInvocations);
271
+ }
272
+
273
+ /**
274
+ * @notice Manually sets the local maximum invocations of project `_projectId`
275
+ * with the provided `_maxInvocations`, checking that `_maxInvocations` is less
276
+ * than or equal to the value of project `_project_id`'s maximum invocations that is
277
+ * set on the core contract.
278
+ * @dev Note that a `_maxInvocations` of 0 can only be set if the current `invocations`
279
+ * value is also 0 and this would also set `maxHasBeenInvoked` to true, correctly short-circuiting
280
+ * this minter's purchase function, avoiding extra gas costs from the core contract's maxInvocations check.
281
+ * @param _projectId Project ID to set the maximum invocations for.
282
+ * @param _maxInvocations Maximum invocations to set for the project.
283
+ */
284
+ function manuallyLimitProjectMaxInvocations(
285
+ uint256 _projectId,
286
+ uint256 _maxInvocations
287
+ ) external onlyArtist(_projectId) {
288
+ // CHECKS
289
+ // ensure that the manually set maxInvocations is not greater than what is set on the core contract
290
+ uint256 maxInvocations;
291
+ uint256 invocations;
292
+ (invocations, maxInvocations, , , , ) = genArtCoreContract_Base
293
+ .projectStateData(_projectId);
294
+ require(
295
+ _maxInvocations <= maxInvocations,
296
+ "Cannot increase project max invocations above core contract set project max invocations"
297
+ );
298
+ require(
299
+ _maxInvocations >= invocations,
300
+ "Cannot set project max invocations to less than current invocations"
301
+ );
302
+
303
+ // EFFECTS
304
+ // update storage with results
305
+ projectConfig[_projectId].maxInvocations = uint24(_maxInvocations);
306
+ // We need to ensure maxHasBeenInvoked is correctly set after manually setting the
307
+ // local maxInvocations value.
308
+ projectConfig[_projectId].maxHasBeenInvoked =
309
+ invocations == _maxInvocations;
310
+
311
+ emit ProjectMaxInvocationsLimitUpdated(_projectId, _maxInvocations);
312
+ }
313
+
314
+ /**
315
+ * @notice Warning: Disabling purchaseTo is not supported on this minter.
316
+ * This method exists purely for interface-conformance purposes.
317
+ */
318
+ function togglePurchaseToDisabled(
319
+ uint256 _projectId
320
+ ) external view onlyArtist(_projectId) {
321
+ revert("Action not supported");
322
+ }
323
+
324
+ /**
325
+ * @notice projectId => has project reached its maximum number of
326
+ * invocations? Note that this returns a local cache of the core contract's
327
+ * state, and may be out of sync with the core contract. This is
328
+ * intentional, as it only enables gas optimization of mints after a
329
+ * project's maximum invocations has been reached. A false negative will
330
+ * only result in a gas cost increase, since the core contract will still
331
+ * enforce a maxInvocation check during minting. A false positive is not
332
+ * possible because the V3 core contract only allows maximum invocations
333
+ * to be reduced, not increased. Based on this rationale, we intentionally
334
+ * do not do input validation in this method as to whether or not the input
335
+ * `_projectId` is an existing project ID.
336
+ */
337
+ function projectMaxHasBeenInvoked(
338
+ uint256 _projectId
339
+ ) external view returns (bool) {
340
+ return projectConfig[_projectId].maxHasBeenInvoked;
341
+ }
342
+
343
+ /**
344
+ * @notice projectId => project's maximum number of invocations.
345
+ * Optionally synced with core contract value, for gas optimization.
346
+ * Note that this returns a local cache of the core contract's
347
+ * state, and may be out of sync with the core contract. This is
348
+ * intentional, as it only enables gas optimization of mints after a
349
+ * project's maximum invocations has been reached.
350
+ * @dev A number greater than the core contract's project max invocations
351
+ * will only result in a gas cost increase, since the core contract will
352
+ * still enforce a maxInvocation check during minting. A number less than
353
+ * the core contract's project max invocations is only possible when the
354
+ * project's max invocations have not been synced on this minter, since the
355
+ * V3 core contract only allows maximum invocations to be reduced, not
356
+ * increased. When this happens, the minter will enable minting, allowing
357
+ * the core contract to enforce the max invocations check. Based on this
358
+ * rationale, we intentionally do not do input validation in this method as
359
+ * to whether or not the input `_projectId` is an existing project ID.
360
+ */
361
+ function projectMaxInvocations(
362
+ uint256 _projectId
363
+ ) external view returns (uint256) {
364
+ return uint256(projectConfig[_projectId].maxInvocations);
365
+ }
366
+
367
+ /**
368
+ * @notice Updates this minter's price per token of project `_projectId`
369
+ * to be '_pricePerTokenInWei`, in Wei.
370
+ * This price supersedes any legacy core contract price per token value.
371
+ * @dev Note that it is intentionally supported here that the configured
372
+ * price may be explicitly set to `0`.
373
+ */
374
+ function updatePricePerTokenInWei(
375
+ uint256 _projectId,
376
+ uint256 _pricePerTokenInWei
377
+ ) external onlyArtist(_projectId) {
378
+ projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
379
+ projectConfig[_projectId].priceIsConfigured = true;
380
+ emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);
381
+ }
382
+
383
+ /**
384
+ * @notice Inactive function - requires Merkle proof to purchase.
385
+ */
386
+ function purchase(uint256) external payable returns (uint256) {
387
+ revert("Must provide Merkle proof");
388
+ }
389
+
390
+ /**
391
+ * @notice Inactive function - requires Merkle proof to purchase.
392
+ */
393
+ function purchaseTo(address, uint256) public payable returns (uint256) {
394
+ revert("Must provide Merkle proof");
395
+ }
396
+
397
+ /**
398
+ * @notice Purchases a token from project `_projectId`.
399
+ * @param _projectId Project ID to mint a token on.
400
+ * @param _proof Merkle proof.
401
+ * @return tokenId Token ID of minted token
402
+ */
403
+ function purchase(
404
+ uint256 _projectId,
405
+ bytes32[] calldata _proof
406
+ ) external payable returns (uint256 tokenId) {
407
+ tokenId = purchaseTo_kem(msg.sender, _projectId, _proof, address(0));
408
+ return tokenId;
409
+ }
410
+
411
+ /**
412
+ * @notice gas-optimized version of purchase(uint256,bytes32[]).
413
+ */
414
+ function purchase_gD5(
415
+ uint256 _projectId,
416
+ bytes32[] calldata _proof
417
+ ) external payable returns (uint256 tokenId) {
418
+ tokenId = purchaseTo_kem(msg.sender, _projectId, _proof, address(0));
419
+ return tokenId;
420
+ }
421
+
422
+ /**
423
+ * @notice Purchases a token from project `_projectId` and sets
424
+ * the token's owner to `_to`.
425
+ * @param _to Address to be the new token's owner.
426
+ * @param _projectId Project ID to mint a token on.
427
+ * @param _proof Merkle proof.
428
+ * @return tokenId Token ID of minted token
429
+ */
430
+ function purchaseTo(
431
+ address _to,
432
+ uint256 _projectId,
433
+ bytes32[] calldata _proof
434
+ ) external payable returns (uint256 tokenId) {
435
+ return purchaseTo_kem(_to, _projectId, _proof, address(0));
436
+ }
437
+
438
+ /**
439
+ * @notice Purchases a token from project `_projectId` and sets
440
+ * the token's owner to `_to`, as a delegate, (the `msg.sender`)
441
+ * on behalf of an explicitly defined vault.
442
+ * @param _to Address to be the new token's owner.
443
+ * @param _projectId Project ID to mint a token on.
444
+ * @param _proof Merkle proof.
445
+ * @param _vault Vault being purchased on behalf of.
446
+ * @return tokenId Token ID of minted token
447
+ */
448
+ function purchaseTo(
449
+ address _to,
450
+ uint256 _projectId,
451
+ bytes32[] calldata _proof,
452
+ address _vault
453
+ ) external payable returns (uint256 tokenId) {
454
+ return purchaseTo_kem(_to, _projectId, _proof, _vault);
455
+ }
456
+
457
+ /**
458
+ * @notice gas-optimized version of
459
+ * purchaseTo(address,uint256,bytes32[],address).
460
+ * @param _to Address to be the new token's owner.
461
+ * @param _projectId Project ID to mint a token on.
462
+ * @param _proof Merkle proof. Must be a valid proof of either `msg.sender`
463
+ * if `_vault` is `address(0)`, or `_vault` if `_vault` is not `address(0)`.
464
+ * @param _vault Vault being purchased on behalf of. Acceptable to be
465
+ * address(0) if no vault.
466
+ */
467
+ function purchaseTo_kem(
468
+ address _to,
469
+ uint256 _projectId,
470
+ bytes32[] calldata _proof,
471
+ address _vault // acceptable to be `address(0)` if no vault
472
+ ) public payable nonReentrant returns (uint256 tokenId) {
473
+ // CHECKS
474
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
475
+
476
+ // Note that `maxHasBeenInvoked` is only checked here to reduce gas
477
+ // consumption after a project has been fully minted.
478
+ // `_projectConfig.maxHasBeenInvoked` is locally cached to reduce
479
+ // gas consumption, but if not in sync with the core contract's value,
480
+ // the core contract also enforces its own max invocation check during
481
+ // minting.
482
+ require(
483
+ !_projectConfig.maxHasBeenInvoked,
484
+ "Maximum number of invocations reached"
485
+ );
486
+
487
+ // load price of token into memory
488
+ uint256 pricePerTokenInWei = _projectConfig.pricePerTokenInWei;
489
+
490
+ require(
491
+ msg.value >= pricePerTokenInWei,
492
+ "Must send minimum value to mint!"
493
+ );
494
+
495
+ // require artist to have configured price of token on this minter
496
+ require(_projectConfig.priceIsConfigured, "Price not configured");
497
+
498
+ // no contract filter since Merkle tree controls allowed addresses
499
+
500
+ // NOTE: delegate-vault handling **begins here**.
501
+
502
+ // handle that the vault may be either the `msg.sender` in the case
503
+ // that there is not a true vault, or may be `_vault` if one is
504
+ // provided explicitly (and it is valid).
505
+ address vault = msg.sender;
506
+ if (_vault != address(0)) {
507
+ // If a vault is provided, it must be valid, otherwise throw rather
508
+ // than optimistically-minting with original `msg.sender`.
509
+ // Note, we do not check `checkDelegateForAll` as well, as it is known
510
+ // to be implicitly checked by calling `checkDelegateForContract`.
511
+ bool isValidDelegee = delegationRegistryContract
512
+ .checkDelegateForContract(
513
+ msg.sender, // delegate
514
+ _vault, // vault
515
+ genArt721CoreAddress // contract
516
+ );
517
+ require(isValidDelegee, "Invalid delegate-vault pairing");
518
+ vault = _vault;
519
+ }
520
+
521
+ // require valid Merkle proof
522
+ require(
523
+ verifyAddress(_projectId, _proof, vault),
524
+ "Invalid Merkle proof"
525
+ );
526
+
527
+ // limit mints per address by project
528
+ uint256 _maxProjectInvocationsPerAddress = _projectConfig
529
+ .useMaxInvocationsPerAddressOverride
530
+ ? _projectConfig.maxInvocationsPerAddressOverride
531
+ : DEFAULT_MAX_INVOCATIONS_PER_ADDRESS;
532
+
533
+ // note that mint limits index off of the `vault` (when applicable)
534
+ require(
535
+ projectUserMintInvocations[_projectId][vault] <
536
+ _maxProjectInvocationsPerAddress ||
537
+ _maxProjectInvocationsPerAddress == 0,
538
+ "Maximum number of invocations per address reached"
539
+ );
540
+
541
+ // EFFECTS
542
+ // increment user's invocations for this project
543
+ unchecked {
544
+ // this will never overflow since user's invocations on a project
545
+ // are limited by the project's max invocations
546
+ projectUserMintInvocations[_projectId][vault]++;
547
+ }
548
+
549
+ // mint token
550
+ tokenId = minterFilter.mint(_to, _projectId, vault);
551
+
552
+ // NOTE: delegate-vault handling **ends here**.
553
+
554
+ // okay if this underflows because if statement will always eval false.
555
+ // this is only for gas optimization (core enforces maxInvocations).
556
+ unchecked {
557
+ if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
558
+ _projectConfig.maxHasBeenInvoked = true;
559
+ }
560
+ }
561
+
562
+ // INTERACTIONS
563
+ splitFundsETH(_projectId, pricePerTokenInWei, genArt721CoreAddress);
564
+
565
+ return tokenId;
566
+ }
567
+
568
+ /**
569
+ * @notice projectId => maximum invocations per allowlisted address. If a
570
+ * a value of 0 is returned, there is no limit on the number of mints per
571
+ * allowlisted address.
572
+ * Default behavior is limit 1 mint per address.
573
+ * This value can be changed at any time by the artist.
574
+ * @dev default value stated above must be updated if the value of
575
+ * CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE is changed.
576
+ */
577
+ function projectMaxInvocationsPerAddress(
578
+ uint256 _projectId
579
+ ) public view returns (uint256) {
580
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
581
+ if (_projectConfig.useMaxInvocationsPerAddressOverride) {
582
+ return uint256(_projectConfig.maxInvocationsPerAddressOverride);
583
+ } else {
584
+ return DEFAULT_MAX_INVOCATIONS_PER_ADDRESS;
585
+ }
586
+ }
587
+
588
+ /**
589
+ * @notice Returns remaining invocations for a given address.
590
+ * If `projectLimitsMintInvocationsPerAddress` is false, individual
591
+ * addresses are only limited by the project's maximum invocations, and a
592
+ * dummy value of zero is returned for `mintInvocationsRemaining`.
593
+ * If `projectLimitsMintInvocationsPerAddress` is true, the quantity of
594
+ * remaining mint invocations for address `_address` is returned as
595
+ * `mintInvocationsRemaining`.
596
+ * Note that mint invocations per address can be changed at any time by the
597
+ * artist of a project.
598
+ * Also note that all mint invocations are limited by a project's maximum
599
+ * invocations as defined on the core contract. This function may return
600
+ * a value greater than the project's remaining invocations.
601
+ */
602
+ function projectRemainingInvocationsForAddress(
603
+ uint256 _projectId,
604
+ address _address
605
+ )
606
+ external
607
+ view
608
+ returns (
609
+ bool projectLimitsMintInvocationsPerAddress,
610
+ uint256 mintInvocationsRemaining
611
+ )
612
+ {
613
+ uint256 maxInvocationsPerAddress = projectMaxInvocationsPerAddress(
614
+ _projectId
615
+ );
616
+ if (maxInvocationsPerAddress == 0) {
617
+ // project does not limit mint invocations per address, so leave
618
+ // `projectLimitsMintInvocationsPerAddress` at solidity initial
619
+ // value of false. Also leave `mintInvocationsRemaining` at
620
+ // solidity initial value of zero, as indicated in this function's
621
+ // documentation.
622
+ } else {
623
+ projectLimitsMintInvocationsPerAddress = true;
624
+ uint256 userMintInvocations = projectUserMintInvocations[
625
+ _projectId
626
+ ][_address];
627
+ // if user has not reached max invocations per address, return
628
+ // remaining invocations
629
+ if (maxInvocationsPerAddress > userMintInvocations) {
630
+ unchecked {
631
+ // will never underflow due to the check above
632
+ mintInvocationsRemaining =
633
+ maxInvocationsPerAddress -
634
+ userMintInvocations;
635
+ }
636
+ }
637
+ // else user has reached their maximum invocations, so leave
638
+ // `mintInvocationsRemaining` at solidity initial value of zero
639
+ }
640
+ }
641
+
642
+ /**
643
+ * @notice Process proof for an address. Returns Merkle root. Included to
644
+ * enable users to easily verify a proof's validity.
645
+ * @param _proof Merkle proof for address.
646
+ * @param _address Address to process.
647
+ * @return merkleRoot Merkle root for `_address` and `_proof`
648
+ */
649
+ function processProofForAddress(
650
+ bytes32[] calldata _proof,
651
+ address _address
652
+ ) external pure returns (bytes32) {
653
+ return _proof.processProofCalldata(hashAddress(_address));
654
+ }
655
+
656
+ /**
657
+ * @notice Gets if price of token is configured, price of minting a
658
+ * token on project `_projectId`, and currency symbol and address to be
659
+ * used as payment. Supersedes any core contract price information.
660
+ * @param _projectId Project ID to get price information for.
661
+ * @return isConfigured true only if token price has been configured on
662
+ * this minter
663
+ * @return tokenPriceInWei current price of token on this minter - invalid
664
+ * if price has not yet been configured
665
+ * @return currencySymbol currency symbol for purchases of project on this
666
+ * minter. This minter always returns "ETH"
667
+ * @return currencyAddress currency address for purchases of project on
668
+ * this minter. This minter always returns null address, reserved for ether
669
+ */
670
+ function getPriceInfo(
671
+ uint256 _projectId
672
+ )
673
+ external
674
+ view
675
+ returns (
676
+ bool isConfigured,
677
+ uint256 tokenPriceInWei,
678
+ string memory currencySymbol,
679
+ address currencyAddress
680
+ )
681
+ {
682
+ ProjectConfig storage _projectConfig = projectConfig[_projectId];
683
+ isConfigured = _projectConfig.priceIsConfigured;
684
+ tokenPriceInWei = _projectConfig.pricePerTokenInWei;
685
+ currencySymbol = "ETH";
686
+ currencyAddress = address(0);
687
+ }
688
+ }