@artblocks/contracts 1.2.1 → 1.3.1

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 (71) hide show
  1. package/artifacts/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2_Web3Call.sol/BytecodeStorageReaderContractV2_Web3Call.json +201 -0
  2. package/artifacts/contracts/BytecodeStorageReaders/UniversalBytecodeStorageReader.sol/UniversalBytecodeStorageReader.json +2 -2
  3. package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +42 -53
  4. package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +2 -2
  5. package/artifacts/contracts/generator/GenArt721GeneratorV0.sol/GenArt721GeneratorV0.json +2 -2
  6. package/artifacts/contracts/interfaces/v0.8.x/IDelegateRegistry.sol/IDelegateRegistry.json +820 -0
  7. package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +13 -0
  8. package/artifacts/contracts/interfaces/v0.8.x/IPMPAugmentHook.sol/IPMPAugmentHook.json +83 -0
  9. package/artifacts/contracts/interfaces/v0.8.x/IPMPConfigureHook.sol/IPMPConfigureHook.json +80 -0
  10. package/artifacts/contracts/interfaces/v0.8.x/IPMPV0.sol/IPMPV0.json +402 -0
  11. package/artifacts/contracts/interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol/IUniversalBytecodeStorageReader.json +13 -0
  12. package/artifacts/contracts/interfaces/v0.8.x/IWeb3Call.sol/IWeb3Call.json +47 -0
  13. package/artifacts/contracts/libs/integration-refs/delegation-registry/DelegateRegistry.sol/DelegateRegistry.json +846 -0
  14. package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryHashes.sol/RegistryHashes.json +10 -0
  15. package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryOps.sol/RegistryOps.json +10 -0
  16. package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryStorage.sol/RegistryStorage.json +10 -0
  17. package/artifacts/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol/DependencyRegistryStorageLib.json +2 -2
  18. package/artifacts/contracts/libs/v0.8.x/ImmutableStringArray.sol/ImmutableStringArray.json +10 -0
  19. package/artifacts/contracts/libs/v0.8.x/V3FlexLib.sol/V3FlexLib.json +2 -2
  20. package/artifacts/contracts/mock/ImmutableStringArrayMock.sol/ImmutableStringArrayMock.json +69 -0
  21. package/artifacts/contracts/mock/MockPMPAugmentHook.sol/MockPMPAugmentHook.json +214 -0
  22. package/artifacts/contracts/mock/MockPMPConfigureHook.sol/MockPMPConfigureHook.json +236 -0
  23. package/artifacts/contracts/web3call/PMPV0.sol/PMPV0.json +629 -0
  24. package/artifacts/contracts/web3call/Web3Call.sol/Web3Call.json +66 -0
  25. package/artifacts/contracts/web3call/augment-hooks/AbstractPMPAugmentHook.sol/AbstractPMPAugmentHook.json +83 -0
  26. package/artifacts/contracts/web3call/augment-hooks/InjectBaseGasFee.sol/InjectBaseGasFee.json +83 -0
  27. package/artifacts/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol/IUniswapV2Pair.json +60 -0
  28. package/artifacts/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol/InjectChromieSquiggleFloorPrice.json +107 -0
  29. package/artifacts/contracts/web3call/augment-hooks/InjectIsNounsDaoMember.sol/InjectIsNounsDaoMember.json +96 -0
  30. package/artifacts/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol/InjectIsProjectFullyMinted.json +83 -0
  31. package/artifacts/contracts/web3call/augment-hooks/InjectPolyptychTokenHash.sol/InjectPolyptychTokenHash.json +141 -0
  32. package/artifacts/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol/InjectTokenHashSeed.json +99 -0
  33. package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwner.sol/InjectTokenOwner.json +99 -0
  34. package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol/InjectTokenOwnerEthBalance.json +83 -0
  35. package/artifacts/contracts/web3call/augment-hooks/InjectUSDCTotalSupply.sol/InjectUSDCTotalSupply.json +107 -0
  36. package/artifacts/contracts/web3call/configure-hooks/abstractPMPConfigureHook.sol/AbstractPMPConfigureHook.json +80 -0
  37. package/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2_Web3Call.sol +128 -0
  38. package/contracts/DependencyRegistryV0.sol +67 -28
  39. package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +7 -1
  40. package/contracts/generator/GenArt721GeneratorV0.sol +77 -8
  41. package/contracts/interfaces/v0.8.x/IDelegateRegistry.sol +318 -0
  42. package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +2 -0
  43. package/contracts/interfaces/v0.8.x/IPMPAugmentHook.sol +31 -0
  44. package/contracts/interfaces/v0.8.x/IPMPConfigureHook.sol +25 -0
  45. package/contracts/interfaces/v0.8.x/IPMPV0.sol +224 -0
  46. package/contracts/interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol +5 -0
  47. package/contracts/interfaces/v0.8.x/IWeb3Call.sol +28 -0
  48. package/contracts/libs/integration-refs/delegation-registry/DelegateRegistry.sol +753 -0
  49. package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryHashes.sol +359 -0
  50. package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryOps.sol +31 -0
  51. package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryStorage.sol +87 -0
  52. package/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol +3 -0
  53. package/contracts/libs/v0.8.x/ImmutableStringArray.sol +301 -0
  54. package/contracts/libs/v0.8.x/V3FlexLib.sol +7 -1
  55. package/contracts/mock/ImmutableStringArrayMock.sol +47 -0
  56. package/contracts/mock/MockPMPAugmentHook.sol +170 -0
  57. package/contracts/mock/MockPMPConfigureHook.sol +78 -0
  58. package/contracts/web3call/PMPV0.sol +1126 -0
  59. package/contracts/web3call/Web3Call.sol +25 -0
  60. package/contracts/web3call/augment-hooks/AbstractPMPAugmentHook.sol +46 -0
  61. package/contracts/web3call/augment-hooks/InjectBaseGasFee.sol +57 -0
  62. package/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol +103 -0
  63. package/contracts/web3call/augment-hooks/InjectIsNounsDaoMember.sol +66 -0
  64. package/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol +73 -0
  65. package/contracts/web3call/augment-hooks/InjectPolyptychTokenHash.sol +88 -0
  66. package/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol +62 -0
  67. package/contracts/web3call/augment-hooks/InjectTokenOwner.sol +60 -0
  68. package/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol +61 -0
  69. package/contracts/web3call/augment-hooks/InjectUSDCTotalSupply.sol +69 -0
  70. package/contracts/web3call/configure-hooks/abstractPMPConfigureHook.sol +41 -0
  71. package/package.json +5 -5
@@ -0,0 +1,301 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ // Created By: Art Blocks Inc.
3
+
4
+ pragma solidity ^0.8.0;
5
+
6
+ import {SSTORE2} from "./SSTORE2.sol"; // Import SSTORE2 library
7
+
8
+ /**
9
+ * @title ImmutableStringArray
10
+ * @author Art Blocks Inc.
11
+ * @notice This library is optimized to store immutable arrays of strings much more efficiently than using native Solidity storage arrays.
12
+ * It uses a single SSTORE2 contract to store the length + packed offsets + packed string data.
13
+ * Allows overwriting the pointer to point to a new immutable array.
14
+ */
15
+ library ImmutableStringArray {
16
+ /**
17
+ * @notice Struct to store the SSTORE2 pointer to the packed string array.
18
+ * @dev dataPointer is the SSTORE2 pointer to the packed string array. Assigned to address(0) if the array is empty.
19
+ */
20
+ struct StringArray {
21
+ address dataPointer; // SSTORE2 pointer storing length + packed offsets + packed string data
22
+ }
23
+
24
+ /**
25
+ * @notice Stores a packed immutable string array using a single SSTORE2 contract.
26
+ * Allows overwriting the pointer to point to a new immutable array.
27
+ * @dev Optimized for the case where the array is empty by assigning the dataPointer to address(0).
28
+ * @param storageArray The storage reference to store the packed array.
29
+ * @param strings The array of strings to pack.
30
+ */
31
+ function store(
32
+ StringArray storage storageArray,
33
+ string[] memory strings
34
+ ) internal {
35
+ uint256 arrayLength = strings.length;
36
+ // edge case - empty array
37
+ if (arrayLength == 0) {
38
+ storageArray.dataPointer = address(0);
39
+ return;
40
+ }
41
+
42
+ uint64[] memory offsets = new uint64[](arrayLength);
43
+
44
+ // compute total bytes length and offsets (ensuring no overflow)
45
+ uint256 totalStringBytesLength;
46
+ for (uint256 i = 0; i < arrayLength; i++) {
47
+ offsets[i] = uint64(totalStringBytesLength);
48
+ totalStringBytesLength += bytes(strings[i]).length;
49
+ }
50
+ // realistically will not overflow uint64, but check for security guarantees
51
+ // @dev no coverage on else (difficulty of triggering)
52
+ require(
53
+ totalStringBytesLength <= type(uint64).max,
54
+ "Offset exceeds uint64 limit"
55
+ );
56
+
57
+ // prepare the combined storage structure (length + packed offsets + packed strings)
58
+ // @dev 8 bytes for length, 8 bytes per offset, totalStringBytesLength bytes for strings
59
+ // over-allocate 32 bytes for memory safety
60
+ // note: shorten length by 32 bytes at end of function before returning
61
+ uint256 packedDataLength = 8 +
62
+ (arrayLength * 8) +
63
+ totalStringBytesLength;
64
+ bytes memory packedData = new bytes(packedDataLength + 0x20);
65
+ uint256 ptr;
66
+
67
+ // store the length of the strings array in the first 8 bytes of the packedData
68
+ assembly ("memory-safe") {
69
+ ptr := add(packedData, 0x20) // pointer to first byte of packedData
70
+ mstore(ptr, shl(192, arrayLength)) // left-align only 8 bytes in the 32-byte slot
71
+ ptr := add(ptr, 8) // move pointer forward by 8 bytes
72
+ }
73
+
74
+ // store offsets in packed `uint64` format
75
+ for (uint256 i = 0; i < arrayLength; i++) {
76
+ uint256 offset = offsets[i];
77
+ assembly ("memory-safe") {
78
+ mstore(ptr, shl(192, offset))
79
+ // move pointer ahead 8 bytes for uint64
80
+ ptr := add(ptr, 8)
81
+ }
82
+ }
83
+
84
+ // pack the strings efficiently, using assembly to copy 32 bytes at a time
85
+ for (uint i = 0; i < arrayLength; i++) {
86
+ bytes memory currentString = bytes(strings[i]);
87
+ uint currentLength = currentString.length;
88
+ uint currentPtr;
89
+
90
+ assembly ("memory-safe") {
91
+ currentPtr := add(currentString, 0x20) // start of current string's data
92
+ }
93
+
94
+ // copy the full 32-byte chunks
95
+ uint chunks = currentLength / 32;
96
+ uint remainder = currentLength % 32;
97
+ // store the full 32 byte chunks
98
+ for (uint j = 0; j < chunks; j++) {
99
+ assembly ("memory-safe") {
100
+ let chunk := mload(currentPtr) // load 32 bytes of the current string
101
+ mstore(ptr, chunk) // store the 32 bytes into the result
102
+ ptr := add(ptr, 0x20) // move the result pointer forward by 32 bytes
103
+ currentPtr := add(currentPtr, 0x20) // move the current string pointer forward by 32 bytes
104
+ }
105
+ }
106
+ // store any partial chunks
107
+ if (remainder > 0) {
108
+ bytes32 chunk;
109
+ assembly ("memory-safe") {
110
+ chunk := mload(currentPtr) // load full 32-byte word
111
+ // safe to write past end of final array - 32 bytes of buffer at end of array
112
+ mstore(ptr, chunk) // store the final chunk's remaining bytes
113
+ ptr := add(ptr, remainder) // move the result pointer forward by the remainder length for next iteration
114
+ }
115
+ }
116
+ }
117
+
118
+ // remove the buffer from the packedData bytes array length
119
+ assembly ("memory-safe") {
120
+ ptr := packedData
121
+ mstore(ptr, packedDataLength)
122
+ }
123
+
124
+ // Store all packed data in a SSTORE2 contract, store result in storage
125
+ storageArray.dataPointer = SSTORE2.write(packedData);
126
+ }
127
+
128
+ /**
129
+ * @notice Retrieves the total number of stored strings from SSTORE2.
130
+ * @param storageArray The storage reference containing packed strings.
131
+ * @return count The count of stored strings.
132
+ */
133
+ function length(
134
+ StringArray storage storageArray
135
+ ) internal view returns (uint256 count) {
136
+ // edge case - unassigned dataPointer or empty array
137
+ if (storageArray.dataPointer == address(0)) {
138
+ return 0;
139
+ }
140
+
141
+ // @dev this could be more efficient by only reading the first 8 bytes of the SSTORE2 contract
142
+ // but prefer to keep simple
143
+ bytes memory allData = SSTORE2.read(storageArray.dataPointer);
144
+
145
+ assembly ("memory-safe") {
146
+ count := shr(192, mload(add(allData, 32)))
147
+ }
148
+ }
149
+
150
+ /**
151
+ * @notice Retrieves a single string from storage by index.
152
+ * @param storageArray The storage reference containing packed strings.
153
+ * @param index The index of the string to retrieve.
154
+ * @return result The retrieved string.
155
+ */
156
+ function get(
157
+ StringArray storage storageArray,
158
+ uint256 index
159
+ ) internal view returns (string memory result) {
160
+ // edge case - unassigned dataPointer or empty array
161
+ if (storageArray.dataPointer == address(0)) {
162
+ revert("Index out of bounds");
163
+ }
164
+ bytes memory allData = SSTORE2.read(storageArray.dataPointer);
165
+ uint256 offsetsStart;
166
+ uint64 start;
167
+ uint64 end;
168
+ // load the total length of the strings array from the first 8 bytes of the SSTORE2 contract
169
+ uint256 arrayLength;
170
+ assembly ("memory-safe") {
171
+ arrayLength := shr(192, mload(add(allData, 32)))
172
+ }
173
+
174
+ require(index < arrayLength, "Index out of bounds");
175
+ if (index + 1 == arrayLength) {
176
+ uint256 allDataLength = allData.length;
177
+ assembly ("memory-safe") {
178
+ offsetsStart := add(allData, 40) // skipping first 8 bytes (length)
179
+ start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // load offsets[index] as uint64
180
+ end := sub(
181
+ allDataLength,
182
+ add(8, mul(arrayLength, 8)) // subtract 8 bytes for overall array length, and 8*length for the offsets section
183
+ )
184
+ }
185
+ } else {
186
+ assembly ("memory-safe") {
187
+ offsetsStart := add(allData, 40) // skipping first 8 bytes (length)
188
+ start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // load offsets[index] as uint64
189
+ end := shr(192, mload(add(offsetsStart, mul(add(index, 1), 8)))) // load offsets[index + 1]
190
+ }
191
+ }
192
+
193
+ uint256 strLength = end - start;
194
+ // over-allocate 32 bytes for memory safety
195
+ bytes memory strBytes = new bytes(strLength + 32);
196
+ uint256 ptr;
197
+ assembly ("memory-safe") {
198
+ ptr := add(strBytes, 0x20) // pointer to first byte of strBytes
199
+ }
200
+
201
+ // efficiently copy 32 bytes at a time from allData to strBytes
202
+ for (uint256 i = 0; i < strLength; i += 32) {
203
+ // offset = 32 bytes for length + 8 bytes for overall array length + 8 bytes per offset * length of array + start of string + i
204
+ uint256 offset = 32 + 8 + (arrayLength * 8) + start + i;
205
+ assembly ("memory-safe") {
206
+ let chunk := mload(add(allData, offset)) // load 32 bytes of the current string
207
+ mstore(ptr, chunk) // store the 32 bytes into the result (buffer keeps this memory safe)
208
+ ptr := add(ptr, 0x20) // move the result pointer forward by 32 bytes
209
+ }
210
+ }
211
+
212
+ // remove the buffer length from the strBytes bytes array length
213
+ assembly ("memory-safe") {
214
+ ptr := strBytes
215
+ mstore(ptr, strLength)
216
+ }
217
+ result = string(strBytes);
218
+ }
219
+
220
+ /**
221
+ * @notice Retrieves all stored strings in a single batch call.
222
+ * @param storageArray The storage reference containing packed strings.
223
+ * @return results An array of all stored strings.
224
+ */
225
+ function getAll(
226
+ StringArray storage storageArray
227
+ ) internal view returns (string[] memory results) {
228
+ // edge case - unassigned dataPointer or empty array
229
+ if (storageArray.dataPointer == address(0)) {
230
+ return new string[](0);
231
+ }
232
+ // load the packed data from the SSTORE2 contract
233
+ bytes memory allData = SSTORE2.read(storageArray.dataPointer);
234
+ // load the total length of the strings array from the first 8 bytes of the SSTORE2 contract
235
+ uint256 arrayLength;
236
+ assembly ("memory-safe") {
237
+ arrayLength := shr(192, mload(add(allData, 32)))
238
+ }
239
+
240
+ // for each string in the array, populate results
241
+ results = new string[](arrayLength);
242
+
243
+ for (uint256 index = 0; index < arrayLength; index++) {
244
+ // @dev start and end are calculated for each index for code simplicity/code re-use from get() function
245
+ uint256 offsetsStart;
246
+ uint64 start;
247
+ uint64 end;
248
+
249
+ if (index + 1 == arrayLength) {
250
+ uint256 allDataLength = allData.length;
251
+ assembly ("memory-safe") {
252
+ offsetsStart := add(allData, 40) // Skipping first 8 bytes (length)
253
+ start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // Load offsets[index] as uint64
254
+ end := sub(
255
+ allDataLength,
256
+ add(8, mul(arrayLength, 8)) // subtract 8 bytes for overall array length, and 8*length for the offsets section
257
+ )
258
+ }
259
+ } else {
260
+ assembly ("memory-safe") {
261
+ offsetsStart := add(allData, 40) // Skipping first 8 bytes (length)
262
+ start := shr(192, mload(add(offsetsStart, mul(index, 8)))) // Load offsets[index] as uint64
263
+ end := shr(
264
+ 192,
265
+ mload(add(offsetsStart, mul(add(index, 1), 8)))
266
+ ) // Load offsets[index + 1]
267
+ }
268
+ }
269
+
270
+ uint256 strLength = end - start;
271
+ // over-allocate 32 bytes for memory safety
272
+ bytes memory strBytes = new bytes(strLength + 32);
273
+ uint256 ptr;
274
+ assembly ("memory-safe") {
275
+ ptr := add(strBytes, 0x20) // pointer to first byte of strBytes
276
+ }
277
+
278
+ // efficiently copy 32 bytes at a time from allData to strBytes
279
+ for (uint256 i = 0; i < strLength; i += 32) {
280
+ // offset = 32 bytes for length + 8 bytes for overall array length + 8 bytes per offset * length of array + start of string + i
281
+ uint256 offset = 32 + 8 + (arrayLength * 8) + start + i;
282
+ assembly ("memory-safe") {
283
+ let chunk := mload(add(allData, offset)) // Load 32 bytes of the current string
284
+ mstore(ptr, chunk) // Store the 32 bytes into the result (buffer keeps this memory safe)
285
+ ptr := add(ptr, 0x20) // Move the result pointer forward by 32 bytes
286
+ }
287
+ }
288
+
289
+ // remove the buffer length from the strBytes bytes array length
290
+ assembly ("memory-safe") {
291
+ ptr := strBytes
292
+ mstore(ptr, strLength)
293
+ }
294
+
295
+ // store the string in the results array
296
+ results[index] = string(strBytes);
297
+ }
298
+
299
+ // results is returned by reference, so no need for explicit return
300
+ }
301
+ }
@@ -262,6 +262,8 @@ library V3FlexLib {
262
262
  * @notice Updates external asset dependency for project `_projectId` at
263
263
  * index `_index`, with data at BytecodeStorage-compatible address
264
264
  * `_assetAddress`.
265
+ * @dev This function may point to any BytecodeStorageReader-compatible
266
+ * on-chain asset, including a web3call contract.
265
267
  * @param _projectId Project to be updated.
266
268
  * @param _index Asset index.
267
269
  * @param _assetAddress Address of the on-chain asset.
@@ -447,6 +449,8 @@ library V3FlexLib {
447
449
  * @notice Adds an on-chain external asset dependency for project
448
450
  * `_projectId`, with data at BytecodeStorage-compatible address
449
451
  * `_assetAddress`.
452
+ * @dev This function may point to any BytecodeStorageReader-compatible
453
+ * on-chain asset, including a web3call contract.
450
454
  * @param _projectId Project to be updated.
451
455
  * @param _assetAddress Address of the BytecodeStorageReader-compatible on-chain asset.
452
456
  */
@@ -500,9 +504,11 @@ library V3FlexLib {
500
504
 
501
505
  /**
502
506
  * @notice Returns external asset dependency for project `_projectId` at index `_index`.
503
- * If the dependencyType is ONCHAIN, the `data` field will contain the extrated bytecode data and `cid`
507
+ * If the dependencyType is ONCHAIN, the `data` field will contain the read BytecodeStorage data and `cid`
504
508
  * will be an empty string. Conversly, for any other dependencyType, the `data` field will be an empty string
505
509
  * and the `bytecodeAddress` will point to the zero address.
510
+ * @dev If an ONCHAIN dependency is a web3call, the `data` field will be returned per the BytecodeStorageReader
511
+ * contract's active spec, which at the time of this writing is the special string "#web3call_contract#".
506
512
  * @param _projectId Project to get external asset dependency for.
507
513
  * @param _index Index of the external asset dependency.
508
514
  * @param _bytecodeStorageReaderContract The bytecode storage reader contract to use for reading on-chain
@@ -0,0 +1,47 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.0;
3
+
4
+ import {ImmutableStringArray} from "../libs/v0.8.x/ImmutableStringArray.sol";
5
+
6
+ /**
7
+ * @title ImmutableStringArrayMock
8
+ * @notice Mock contract for testing ImmutableStringArray library functionality
9
+ */
10
+ contract ImmutableStringArrayMock {
11
+ using ImmutableStringArray for ImmutableStringArray.StringArray;
12
+
13
+ ImmutableStringArray.StringArray private stringArray;
14
+
15
+ /**
16
+ * @notice Store an array of strings
17
+ * @param strings The array of strings to store
18
+ */
19
+ function store(string[] memory strings) external {
20
+ stringArray.store(strings);
21
+ }
22
+
23
+ /**
24
+ * @notice Get the length of the stored array
25
+ * @return The number of strings stored
26
+ */
27
+ function length() external view returns (uint256) {
28
+ return stringArray.length();
29
+ }
30
+
31
+ /**
32
+ * @notice Get a string at a specific index
33
+ * @param index The index to retrieve
34
+ * @return The string at the given index
35
+ */
36
+ function get(uint256 index) external view returns (string memory) {
37
+ return stringArray.get(index);
38
+ }
39
+
40
+ /**
41
+ * @notice Get all stored strings
42
+ * @return An array containing all stored strings
43
+ */
44
+ function getAll() external view returns (string[] memory) {
45
+ return stringArray.getAll();
46
+ }
47
+ }
@@ -0,0 +1,170 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ pragma solidity 0.8.22;
3
+
4
+ import "../interfaces/v0.8.x/IPMPAugmentHook.sol";
5
+ import "../interfaces/v0.8.x/IWeb3Call.sol";
6
+ import "@openzeppelin-5.0/contracts/utils/introspection/ERC165.sol";
7
+
8
+ /**
9
+ * @title Mock PMP Augment Hook
10
+ * @notice Mock implementation of IPMPAugmentHook for testing purposes
11
+ */
12
+ contract MockPMPAugmentHook is IPMPAugmentHook, ERC165 {
13
+ // Event to track when the hook is called
14
+ event TokenPMPAugmented(
15
+ address indexed coreContract,
16
+ uint256 indexed tokenId,
17
+ uint256 paramCount
18
+ );
19
+
20
+ // Return behavior settings
21
+ bool public shouldAddParam = false;
22
+ bool public shouldModifyParams = false;
23
+ bool public shouldRevert = false;
24
+
25
+ // Parameters for augmentation
26
+ string public augmentKey = "augmentedParam";
27
+ string public augmentValue = "augmentedValue";
28
+
29
+ /**
30
+ * @notice Configure the hook behavior
31
+ * @param _shouldAddParam Whether the hook should add a parameter
32
+ * @param _shouldModifyParams Whether the hook should modify existing parameters
33
+ * @param _shouldRevert Whether the hook should revert
34
+ */
35
+ function setHookBehavior(
36
+ bool _shouldAddParam,
37
+ bool _shouldModifyParams,
38
+ bool _shouldRevert
39
+ ) external {
40
+ shouldAddParam = _shouldAddParam;
41
+ shouldModifyParams = _shouldModifyParams;
42
+ shouldRevert = _shouldRevert;
43
+ }
44
+
45
+ /**
46
+ * @notice Set the augmentation values
47
+ * @param _augmentKey The key for the augmented parameter
48
+ * @param _augmentValue The value for the augmented parameter
49
+ */
50
+ function setAugmentValues(
51
+ string memory _augmentKey,
52
+ string memory _augmentValue
53
+ ) external {
54
+ augmentKey = _augmentKey;
55
+ augmentValue = _augmentValue;
56
+ }
57
+
58
+ /**
59
+ * @notice Implement the hook method that gets called during token PMP reads
60
+ * @param tokenParams The original token parameters
61
+ * @return The augmented token parameters
62
+ */
63
+ function onTokenPMPReadAugmentation(
64
+ address /* coreContract */,
65
+ uint256 /* tokenId */,
66
+ IWeb3Call.TokenParam[] calldata tokenParams
67
+ ) external view override returns (IWeb3Call.TokenParam[] memory) {
68
+ // Since this is a view function, we can't emit events or modify state
69
+ // Tests will need to call the hook and then separately call simulateCall
70
+
71
+ // Optionally revert to test error handling
72
+ if (shouldRevert) {
73
+ revert("MockPMPAugmentHook: Intentional revert");
74
+ }
75
+
76
+ // If we shouldn't modify, just return the original
77
+ if (!shouldAddParam && !shouldModifyParams) {
78
+ IWeb3Call.TokenParam[] memory result_ = new IWeb3Call.TokenParam[](
79
+ tokenParams.length
80
+ );
81
+ for (uint256 i = 0; i < tokenParams.length; i++) {
82
+ result_[i] = tokenParams[i];
83
+ }
84
+ return result_;
85
+ }
86
+
87
+ // Handle parameter modification
88
+ if (shouldModifyParams && tokenParams.length > 0) {
89
+ // Create a copy to modify
90
+ IWeb3Call.TokenParam[]
91
+ memory modifiedParams = new IWeb3Call.TokenParam[](
92
+ tokenParams.length
93
+ );
94
+
95
+ // Copy all parameters
96
+ for (uint256 i = 0; i < tokenParams.length; i++) {
97
+ modifiedParams[i] = tokenParams[i];
98
+ }
99
+
100
+ // Modify first parameter
101
+ if (tokenParams.length > 0) {
102
+ modifiedParams[0].value = string(
103
+ abi.encodePacked("modified_", tokenParams[0].value)
104
+ );
105
+ }
106
+
107
+ // Handle parameter addition if needed
108
+ if (shouldAddParam) {
109
+ // Create new array with one additional element
110
+ IWeb3Call.TokenParam[]
111
+ memory augmentedParams = new IWeb3Call.TokenParam[](
112
+ modifiedParams.length + 1
113
+ );
114
+
115
+ // Copy existing modified parameters
116
+ for (uint256 i = 0; i < modifiedParams.length; i++) {
117
+ augmentedParams[i] = modifiedParams[i];
118
+ }
119
+
120
+ // Add new parameter
121
+ augmentedParams[modifiedParams.length] = IWeb3Call.TokenParam({
122
+ key: augmentKey,
123
+ value: augmentValue
124
+ });
125
+
126
+ return augmentedParams;
127
+ }
128
+
129
+ return modifiedParams;
130
+ }
131
+
132
+ // Handle parameter addition only
133
+ if (shouldAddParam) {
134
+ // Create new array with one additional element
135
+ IWeb3Call.TokenParam[]
136
+ memory augmentedParams = new IWeb3Call.TokenParam[](
137
+ tokenParams.length + 1
138
+ );
139
+
140
+ // Copy existing parameters
141
+ for (uint256 i = 0; i < tokenParams.length; i++) {
142
+ augmentedParams[i] = tokenParams[i];
143
+ }
144
+
145
+ // Add new parameter
146
+ augmentedParams[tokenParams.length] = IWeb3Call.TokenParam({
147
+ key: augmentKey,
148
+ value: augmentValue
149
+ });
150
+
151
+ return augmentedParams;
152
+ }
153
+
154
+ // This should never be reached due to the if checks above
155
+ revert("MockPMPAugmentHook: Unexpected behavior");
156
+ }
157
+
158
+ /**
159
+ * @notice Implement ERC165 interface detection
160
+ * @param interfaceId The interface ID to check
161
+ * @return True if the interface is supported
162
+ */
163
+ function supportsInterface(
164
+ bytes4 interfaceId
165
+ ) public view override(ERC165, IERC165) returns (bool) {
166
+ return
167
+ interfaceId == type(IPMPAugmentHook).interfaceId ||
168
+ super.supportsInterface(interfaceId);
169
+ }
170
+ }
@@ -0,0 +1,78 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ pragma solidity 0.8.22;
3
+
4
+ import "../interfaces/v0.8.x/IPMPConfigureHook.sol";
5
+ import {IPMPV0} from "../interfaces/v0.8.x/IPMPV0.sol";
6
+ import "@openzeppelin-5.0/contracts/utils/introspection/ERC165.sol";
7
+
8
+ /**
9
+ * @title Mock PMP Configure Hook
10
+ * @notice Mock implementation of IPMPConfigureHook for testing purposes
11
+ */
12
+ contract MockPMPConfigureHook is IPMPConfigureHook, ERC165 {
13
+ // Event to track when the hook is called
14
+ event TokenPMPConfigured(
15
+ address indexed coreContract,
16
+ uint256 indexed tokenId,
17
+ IPMPV0.PMPInput pmpInput
18
+ );
19
+
20
+ // Track last interaction for testing purposes
21
+ address public lastCoreContract;
22
+ uint256 public lastTokenId;
23
+ string public lastPmpKey;
24
+ bytes32 public lastConfiguredValue;
25
+ string public lastConfiguredValueString;
26
+ bool public lastConfiguringArtistString;
27
+
28
+ // Support flag - can be set to simulate failure scenarios
29
+ bool public shouldRevert = false;
30
+
31
+ /**
32
+ * @notice Sets whether the hook should revert on calls
33
+ * @param _shouldRevert True if the hook should revert when called
34
+ */
35
+ function setShouldRevert(bool _shouldRevert) external {
36
+ shouldRevert = _shouldRevert;
37
+ }
38
+
39
+ /**
40
+ * @notice Implement the hook method that gets called after token PMP configuration
41
+ * @param coreContract The address of the core contract
42
+ * @param tokenId The token ID that was configured
43
+ * @param pmpInput The PMP input that was used for configuration
44
+ */
45
+ function onTokenPMPConfigure(
46
+ address coreContract,
47
+ uint256 tokenId,
48
+ IPMPV0.PMPInput memory pmpInput
49
+ ) external override {
50
+ // Store the information for testing
51
+ lastCoreContract = coreContract;
52
+ lastTokenId = tokenId;
53
+ lastPmpKey = pmpInput.key;
54
+ lastConfiguredValue = pmpInput.configuredValue;
55
+ lastConfiguredValueString = pmpInput.configuredValueString;
56
+ lastConfiguringArtistString = pmpInput.configuringArtistString;
57
+
58
+ emit TokenPMPConfigured(coreContract, tokenId, pmpInput);
59
+
60
+ // Optionally revert to test error handling
61
+ if (shouldRevert) {
62
+ revert("MockPMPConfigureHook: Intentional revert");
63
+ }
64
+ }
65
+
66
+ /**
67
+ * @notice Implement ERC165 interface detection
68
+ * @param interfaceId The interface ID to check
69
+ * @return True if the interface is supported
70
+ */
71
+ function supportsInterface(
72
+ bytes4 interfaceId
73
+ ) public view override(ERC165, IERC165) returns (bool) {
74
+ return
75
+ interfaceId == type(IPMPConfigureHook).interfaceId ||
76
+ super.supportsInterface(interfaceId);
77
+ }
78
+ }