@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.
- package/artifacts/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2_Web3Call.sol/BytecodeStorageReaderContractV2_Web3Call.json +201 -0
- package/artifacts/contracts/BytecodeStorageReaders/UniversalBytecodeStorageReader.sol/UniversalBytecodeStorageReader.json +2 -2
- package/artifacts/contracts/DependencyRegistryV0.sol/DependencyRegistryV0.json +42 -53
- package/artifacts/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol/GenArt721CoreV3_Engine_Flex.json +2 -2
- package/artifacts/contracts/generator/GenArt721GeneratorV0.sol/GenArt721GeneratorV0.json +2 -2
- package/artifacts/contracts/interfaces/v0.8.x/IDelegateRegistry.sol/IDelegateRegistry.json +820 -0
- package/artifacts/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol/IDependencyRegistryV0.json +13 -0
- package/artifacts/contracts/interfaces/v0.8.x/IPMPAugmentHook.sol/IPMPAugmentHook.json +83 -0
- package/artifacts/contracts/interfaces/v0.8.x/IPMPConfigureHook.sol/IPMPConfigureHook.json +80 -0
- package/artifacts/contracts/interfaces/v0.8.x/IPMPV0.sol/IPMPV0.json +402 -0
- package/artifacts/contracts/interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol/IUniversalBytecodeStorageReader.json +13 -0
- package/artifacts/contracts/interfaces/v0.8.x/IWeb3Call.sol/IWeb3Call.json +47 -0
- package/artifacts/contracts/libs/integration-refs/delegation-registry/DelegateRegistry.sol/DelegateRegistry.json +846 -0
- package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryHashes.sol/RegistryHashes.json +10 -0
- package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryOps.sol/RegistryOps.json +10 -0
- package/artifacts/contracts/libs/integration-refs/delegation-registry/libraries/RegistryStorage.sol/RegistryStorage.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol/DependencyRegistryStorageLib.json +2 -2
- package/artifacts/contracts/libs/v0.8.x/ImmutableStringArray.sol/ImmutableStringArray.json +10 -0
- package/artifacts/contracts/libs/v0.8.x/V3FlexLib.sol/V3FlexLib.json +2 -2
- package/artifacts/contracts/mock/ImmutableStringArrayMock.sol/ImmutableStringArrayMock.json +69 -0
- package/artifacts/contracts/mock/MockPMPAugmentHook.sol/MockPMPAugmentHook.json +214 -0
- package/artifacts/contracts/mock/MockPMPConfigureHook.sol/MockPMPConfigureHook.json +236 -0
- package/artifacts/contracts/web3call/PMPV0.sol/PMPV0.json +629 -0
- package/artifacts/contracts/web3call/Web3Call.sol/Web3Call.json +66 -0
- package/artifacts/contracts/web3call/augment-hooks/AbstractPMPAugmentHook.sol/AbstractPMPAugmentHook.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectBaseGasFee.sol/InjectBaseGasFee.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol/IUniswapV2Pair.json +60 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol/InjectChromieSquiggleFloorPrice.json +107 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectIsNounsDaoMember.sol/InjectIsNounsDaoMember.json +96 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol/InjectIsProjectFullyMinted.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectPolyptychTokenHash.sol/InjectPolyptychTokenHash.json +141 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol/InjectTokenHashSeed.json +99 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwner.sol/InjectTokenOwner.json +99 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol/InjectTokenOwnerEthBalance.json +83 -0
- package/artifacts/contracts/web3call/augment-hooks/InjectUSDCTotalSupply.sol/InjectUSDCTotalSupply.json +107 -0
- package/artifacts/contracts/web3call/configure-hooks/abstractPMPConfigureHook.sol/AbstractPMPConfigureHook.json +80 -0
- package/contracts/BytecodeStorageReaders/BytecodeStorageReaderContractV2_Web3Call.sol +128 -0
- package/contracts/DependencyRegistryV0.sol +67 -28
- package/contracts/engine/V3/GenArt721CoreV3_Engine_Flex.sol +7 -1
- package/contracts/generator/GenArt721GeneratorV0.sol +77 -8
- package/contracts/interfaces/v0.8.x/IDelegateRegistry.sol +318 -0
- package/contracts/interfaces/v0.8.x/IDependencyRegistryV0.sol +2 -0
- package/contracts/interfaces/v0.8.x/IPMPAugmentHook.sol +31 -0
- package/contracts/interfaces/v0.8.x/IPMPConfigureHook.sol +25 -0
- package/contracts/interfaces/v0.8.x/IPMPV0.sol +224 -0
- package/contracts/interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol +5 -0
- package/contracts/interfaces/v0.8.x/IWeb3Call.sol +28 -0
- package/contracts/libs/integration-refs/delegation-registry/DelegateRegistry.sol +753 -0
- package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryHashes.sol +359 -0
- package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryOps.sol +31 -0
- package/contracts/libs/integration-refs/delegation-registry/libraries/RegistryStorage.sol +87 -0
- package/contracts/libs/v0.8.x/DependencyRegistryStorageLib.sol +3 -0
- package/contracts/libs/v0.8.x/ImmutableStringArray.sol +301 -0
- package/contracts/libs/v0.8.x/V3FlexLib.sol +7 -1
- package/contracts/mock/ImmutableStringArrayMock.sol +47 -0
- package/contracts/mock/MockPMPAugmentHook.sol +170 -0
- package/contracts/mock/MockPMPConfigureHook.sol +78 -0
- package/contracts/web3call/PMPV0.sol +1126 -0
- package/contracts/web3call/Web3Call.sol +25 -0
- package/contracts/web3call/augment-hooks/AbstractPMPAugmentHook.sol +46 -0
- package/contracts/web3call/augment-hooks/InjectBaseGasFee.sol +57 -0
- package/contracts/web3call/augment-hooks/InjectChromieSquiggleFloorPrice.sol +103 -0
- package/contracts/web3call/augment-hooks/InjectIsNounsDaoMember.sol +66 -0
- package/contracts/web3call/augment-hooks/InjectIsProjectFullyMinted.sol +73 -0
- package/contracts/web3call/augment-hooks/InjectPolyptychTokenHash.sol +88 -0
- package/contracts/web3call/augment-hooks/InjectTokenHashSeed.sol +62 -0
- package/contracts/web3call/augment-hooks/InjectTokenOwner.sol +60 -0
- package/contracts/web3call/augment-hooks/InjectTokenOwnerEthBalance.sol +61 -0
- package/contracts/web3call/augment-hooks/InjectUSDCTotalSupply.sol +69 -0
- package/contracts/web3call/configure-hooks/abstractPMPConfigureHook.sol +41 -0
- 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
|
|
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
|
+
}
|