@artblocks/contracts 1.2.1 → 1.3.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 (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 +289 -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 +1125 -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,80 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "AbstractPMPConfigureHook",
4
+ "sourceName": "contracts/web3call/configure-hooks/abstractPMPConfigureHook.sol",
5
+ "abi": [
6
+ {
7
+ "inputs": [
8
+ {
9
+ "internalType": "address",
10
+ "name": "coreContract",
11
+ "type": "address"
12
+ },
13
+ {
14
+ "internalType": "uint256",
15
+ "name": "tokenId",
16
+ "type": "uint256"
17
+ },
18
+ {
19
+ "components": [
20
+ {
21
+ "internalType": "string",
22
+ "name": "key",
23
+ "type": "string"
24
+ },
25
+ {
26
+ "internalType": "enum IPMPV0.ParamType",
27
+ "name": "configuredParamType",
28
+ "type": "uint8"
29
+ },
30
+ {
31
+ "internalType": "bytes32",
32
+ "name": "configuredValue",
33
+ "type": "bytes32"
34
+ },
35
+ {
36
+ "internalType": "bool",
37
+ "name": "configuringArtistString",
38
+ "type": "bool"
39
+ },
40
+ {
41
+ "internalType": "string",
42
+ "name": "configuredValueString",
43
+ "type": "string"
44
+ }
45
+ ],
46
+ "internalType": "struct IPMPV0.PMPInput",
47
+ "name": "pmpInput",
48
+ "type": "tuple"
49
+ }
50
+ ],
51
+ "name": "onTokenPMPConfigure",
52
+ "outputs": [],
53
+ "stateMutability": "nonpayable",
54
+ "type": "function"
55
+ },
56
+ {
57
+ "inputs": [
58
+ {
59
+ "internalType": "bytes4",
60
+ "name": "interfaceId",
61
+ "type": "bytes4"
62
+ }
63
+ ],
64
+ "name": "supportsInterface",
65
+ "outputs": [
66
+ {
67
+ "internalType": "bool",
68
+ "name": "",
69
+ "type": "bool"
70
+ }
71
+ ],
72
+ "stateMutability": "view",
73
+ "type": "function"
74
+ }
75
+ ],
76
+ "bytecode": "0x",
77
+ "deployedBytecode": "0x",
78
+ "linkReferences": {},
79
+ "deployedLinkReferences": {}
80
+ }
@@ -0,0 +1,128 @@
1
+ // SPDX-License-Identifier: LGPL-3.0-only
2
+ pragma solidity 0.8.22;
3
+
4
+ // Created By: Art Blocks Inc.
5
+
6
+ import {IBytecodeStorageReaderV2} from "../interfaces/v0.8.x/IBytecodeStorageReaderV2.sol";
7
+ import {IWeb3Call} from "../interfaces/v0.8.x/IWeb3Call.sol";
8
+
9
+ import {BytecodeStorageReader} from "../libs/v0.8.x/BytecodeStorageV2.sol";
10
+
11
+ import {ERC165Checker} from "@openzeppelin-5.0/contracts/utils/introspection/ERC165Checker.sol";
12
+
13
+ /**
14
+ * @title Art Blocks Bytecode Storage Reader Contract V2 w/ Web3Call Support
15
+ * @author Art Blocks Inc.
16
+ * @notice This contract is used to read the bytecode of a contract deployed by Art Blocks' BytecodeStorage library,
17
+ * for versions 0, 1, and 2 of the Art Blocks BytecodeStorage library.
18
+ * It includes support for returning the special string "#web3call_contract#" when reading a non-bytecode storage
19
+ * contract that does indicate ERC165 support for the IWeb3Call interface, enabling backwards-compatibility with
20
+ * Web3Call-based projects on exiting Art Blocks contracts. This only affects the readFromBytecode function.
21
+ * It is permissionless and can be used by anyone to read the bytecode of any contract deployed by Art Blocks.
22
+ * Future versions of BytecodeStorage will not be compatible with this reader.
23
+ * For a single location to read current and future versions of the Art Blocks BytecodeStorage library, see
24
+ * the UniversalBytecodeStorageReader contract, maintained by the Art Blocks team.
25
+ * Additional functionality is provided to read bytes data stored using the SSTORE2 library.
26
+ * This contract also provides an interface to read additional metadata stored by the Art Blocks BytecodeStorage
27
+ * library, such as the contract's version, deployer, and other metadata.
28
+ * @dev For simplicity and reduce code changes, this contract uses the BytecodeStorageReader external library to
29
+ * perform many operations. Future reader contract versions may choose to make the library internal to this contract to
30
+ * improve read operation performance.
31
+ */
32
+ contract BytecodeStorageReaderContractV2_Web3Call is IBytecodeStorageReaderV2 {
33
+ using BytecodeStorageReader for address;
34
+
35
+ string public constant VERSION = "BytecodeStorageReaderContractV2_Web3Call";
36
+
37
+ /**
38
+ * @notice Read a string from a data contract deployed via BytecodeStorage V2 or earlier.
39
+ * Also supports returning "#web3call_contract#" when the contract is not a
40
+ * BytecodeStorage contract, but does support the IWeb3Call interface.
41
+ * Reverts if the contract is not a BytecodeStorage contract and does not indicate
42
+ * ERC165 support for the IWeb3Call interface.
43
+ * @param address_ address of contract deployed via BytecodeStorage to be read
44
+ * @return The string data stored at the specific address.
45
+ */
46
+ function readFromBytecode(
47
+ address address_
48
+ ) external view returns (string memory) {
49
+ try address_.readFromBytecode() returns (string memory data) {
50
+ return data;
51
+ } catch {
52
+ if (
53
+ ERC165Checker.supportsInterface(
54
+ address_,
55
+ type(IWeb3Call).interfaceId
56
+ )
57
+ ) {
58
+ return "#web3call_contract#";
59
+ }
60
+ }
61
+ // reading from bytecode failed, and does not indicate Web3Call support, so revert
62
+ revert(
63
+ "BytecodeStorageReaderContractV2_Web3Call: Failed to read from bytecode"
64
+ );
65
+ }
66
+
67
+ /**
68
+ * @notice Read a bytes array from the SSTORE2-formatted bytecode of a contract.
69
+ * Note that this function is only compatible with contracts that have been deployed using the popular SSTORE2
70
+ * library.
71
+ * @param address_ address of contract with SSTORE2-formatted bytecode to be read
72
+ * @return data The bytes data stored at the specific address.
73
+ */
74
+ function readBytesFromSSTORE2Bytecode(
75
+ address address_
76
+ ) external view returns (bytes memory) {
77
+ return address_.readBytesFromSSTORE2Bytecode();
78
+ }
79
+
80
+ /**
81
+ * @notice Read a bytes array from the a contract deployed via BytecodeStorage V2 or earlier.
82
+ * @param address_ address of contract with valid bytecode to be read
83
+ * @param offset offset to read from in contract bytecode, explicitly provided (not calculated)
84
+ * @return The bytes data stored at the specific address.
85
+ */
86
+ function readBytesFromBytecode(
87
+ address address_,
88
+ uint256 offset
89
+ ) external view returns (bytes memory) {
90
+ return address_.readBytesFromBytecode(offset);
91
+ }
92
+
93
+ //------ Metadata Functions ------//
94
+
95
+ /**
96
+ * @notice Get address of deployer for given contract deployed via BytecodeStorage V2 or earlier.
97
+ * @param address_ address of deployed contract
98
+ * @return writerAddress address of deployer
99
+ */
100
+ function getWriterAddressForBytecode(
101
+ address address_
102
+ ) external view returns (address) {
103
+ return address_.getWriterAddressForBytecode();
104
+ }
105
+
106
+ /**
107
+ * @notice Get version for given contract deployed via BytecodeStorage V2 or earlier.
108
+ * @param address_ address of deployed contract
109
+ * @return version version of the contract
110
+ */
111
+ function getLibraryVersionForBytecode(
112
+ address address_
113
+ ) external view returns (bytes32) {
114
+ return address_.getLibraryVersionForBytecode();
115
+ }
116
+
117
+ /**
118
+ * @notice Get if data are stored in compressed format for given contract deployed via BytecodeStorage V2 or
119
+ * earlier.
120
+ * @param _address address of deployed contract
121
+ * @return isCompressed boolean indicating if the stored data are compressed
122
+ */
123
+ function getIsCompressedForBytecode(
124
+ address _address
125
+ ) external view returns (bool) {
126
+ return _address.getIsCompressedForBytecode();
127
+ }
128
+ }
@@ -7,6 +7,8 @@ import "./interfaces/v0.8.x/IAdminACLV0.sol";
7
7
  import "./interfaces/v0.8.x/IDependencyRegistryCompatibleV0.sol";
8
8
  import "./interfaces/v0.8.x/IDependencyRegistryV0.sol";
9
9
  import "./interfaces/v0.8.x/ICoreRegistryV1.sol";
10
+ import "./interfaces/v0.8.x/IBytecodeStorageReaderV2.sol";
11
+ import "./interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol";
10
12
 
11
13
  import "@openzeppelin-4.7/contracts/utils/Strings.sol";
12
14
  import "@openzeppelin-4.7/contracts/utils/structs/EnumerableSet.sol";
@@ -15,7 +17,7 @@ import "@openzeppelin-4.8/contracts-upgradeable/proxy/utils/Initializable.sol";
15
17
  import "@openzeppelin-4.5/contracts/utils/math/SafeCast.sol";
16
18
 
17
19
  import "./libs/v0.8.x/DependencyRegistryStorageLib.sol";
18
- import "./libs/v0.8.x/BytecodeStorageV1.sol";
20
+ import "./libs/v0.8.x/BytecodeStorageV2.sol";
19
21
  import "./libs/v0.8.x/Bytes32Strings.sol";
20
22
 
21
23
  /**
@@ -112,6 +114,15 @@ contract DependencyRegistryV0 is
112
114
  _transferOwnership(adminACLContract_);
113
115
  }
114
116
 
117
+ function updateUniversalBytecodeStorageReader(address newReader) external {
118
+ _onlyAdminACL(this.updateUniversalBytecodeStorageReader.selector);
119
+ _onlyNonZeroAddress(newReader);
120
+ DependencyRegistryStorageLib
121
+ .s()
122
+ .universalReader = IUniversalBytecodeStorageReader(newReader);
123
+ emit UniversalBytecodeStorageReaderUpdated(newReader);
124
+ }
125
+
115
126
  /**
116
127
  * @notice Updates the core registry address.
117
128
  * @param _coreRegistryAddress Address of core registry contract.
@@ -969,18 +980,7 @@ contract DependencyRegistryV0 is
969
980
  });
970
981
 
971
982
  address licenseAddress = licenseEntry.licenseBytecodeAddresses[index];
972
- bytes32 storageVersion = BytecodeStorageReader
973
- .getLibraryVersionForBytecode(licenseAddress);
974
- if (storageVersion == BytecodeStorageReader.UNKNOWN_VERSION_STRING) {
975
- return
976
- string(
977
- BytecodeStorageReader.readBytesFromSSTORE2Bytecode(
978
- licenseAddress
979
- )
980
- );
981
- } else {
982
- return BytecodeStorageReader.readFromBytecode(licenseAddress);
983
- }
983
+ return _readFromBytecodeWithSSTORE2Fallback(licenseAddress);
984
984
  }
985
985
 
986
986
  /**
@@ -1250,9 +1250,10 @@ contract DependencyRegistryV0 is
1250
1250
  .dependencyRecords[dependencyNameAndVersion];
1251
1251
  _onlyInRangeIndex({index: index, length: dependency.scriptCount});
1252
1252
  return
1253
- BytecodeStorageReader.getLibraryVersionForBytecode(
1254
- dependency.scriptBytecodeAddresses[index]
1255
- );
1253
+ _getActiveBytecodeStorageReaderContract()
1254
+ .getLibraryVersionForBytecode(
1255
+ dependency.scriptBytecodeAddresses[index]
1256
+ );
1256
1257
  }
1257
1258
 
1258
1259
  /**
@@ -1279,19 +1280,8 @@ contract DependencyRegistryV0 is
1279
1280
  _onlyInRangeIndex({index: index, length: dependency.scriptCount});
1280
1281
 
1281
1282
  address scriptAddress = dependency.scriptBytecodeAddresses[index];
1282
- bytes32 storageVersion = BytecodeStorageReader
1283
- .getLibraryVersionForBytecode(scriptAddress);
1284
1283
 
1285
- if (storageVersion == BytecodeStorageReader.UNKNOWN_VERSION_STRING) {
1286
- return
1287
- string(
1288
- BytecodeStorageReader.readBytesFromSSTORE2Bytecode(
1289
- scriptAddress
1290
- )
1291
- );
1292
- } else {
1293
- return BytecodeStorageReader.readFromBytecode(scriptAddress);
1294
- }
1284
+ return _readFromBytecodeWithSSTORE2Fallback(scriptAddress);
1295
1285
  }
1296
1286
 
1297
1287
  /**
@@ -1329,6 +1319,14 @@ contract DependencyRegistryV0 is
1329
1319
  }
1330
1320
  }
1331
1321
 
1322
+ /**
1323
+ * @notice returns the address of the core registry currently being used by the dependency registry.
1324
+ * @return address Address of the core registry contract.
1325
+ */
1326
+ function currentCoreRegistry() external view returns (address) {
1327
+ return address(DependencyRegistryStorageLib.s().coreRegistryContract);
1328
+ }
1329
+
1332
1330
  /**
1333
1331
  * @notice utility function to convert from string to bytes32.
1334
1332
  * Useful when a human is calling functions that take bytes32 as input.
@@ -1463,4 +1461,45 @@ contract DependencyRegistryV0 is
1463
1461
  newOwner
1464
1462
  );
1465
1463
  }
1464
+
1465
+ /**
1466
+ * @notice Reads data from bytecode with SSTORE2 fallback.
1467
+ * @param dataAddress Address of the data to read.
1468
+ * @return data String representation of the data.
1469
+ */
1470
+ function _readFromBytecodeWithSSTORE2Fallback(
1471
+ address dataAddress
1472
+ ) internal view returns (string memory) {
1473
+ IBytecodeStorageReaderV2 activeReader = _getActiveBytecodeStorageReaderContract();
1474
+ bytes32 storageVersion = activeReader.getLibraryVersionForBytecode(
1475
+ dataAddress
1476
+ );
1477
+ if (storageVersion == BytecodeStorageReader.UNKNOWN_VERSION_STRING) {
1478
+ return
1479
+ string(activeReader.readBytesFromSSTORE2Bytecode(dataAddress));
1480
+ } else {
1481
+ return activeReader.readFromBytecode(dataAddress);
1482
+ }
1483
+ }
1484
+
1485
+ /**
1486
+ * @notice Returns the active bytecode storage reader contract, being used by the universal reader contract.
1487
+ * @dev assumes the versioned reader is IBytecodeStorageReaderV2 or higher.
1488
+ * @return IBytecodeStorageReaderV2 The active bytecode storage reader contract.
1489
+ */
1490
+ function _getActiveBytecodeStorageReaderContract()
1491
+ internal
1492
+ view
1493
+ returns (IBytecodeStorageReaderV2)
1494
+ {
1495
+ return
1496
+ IBytecodeStorageReaderV2(
1497
+ address(
1498
+ DependencyRegistryStorageLib
1499
+ .s()
1500
+ .universalReader
1501
+ .activeBytecodeStorageReaderContract()
1502
+ )
1503
+ );
1504
+ }
1466
1505
  }
@@ -527,6 +527,8 @@ contract GenArt721CoreV3_Engine_Flex is
527
527
  * @notice Updates external asset dependency for project `_projectId` at
528
528
  * index `_index`, with data at BytecodeStorage-compatible address
529
529
  * `_assetAddress`.
530
+ * @dev This function may point to any BytecodeStorageReader-compatible
531
+ * on-chain asset, including a web3call contract.
530
532
  * @param _projectId Project to be updated.
531
533
  * @param _index Asset index.
532
534
  * @param _assetAddress Address of the on-chain asset.
@@ -624,6 +626,8 @@ contract GenArt721CoreV3_Engine_Flex is
624
626
  * @notice Adds an on-chain external asset dependency for project
625
627
  * `_projectId`, with data at BytecodeStorage-compatible address
626
628
  * `_assetAddress`.
629
+ * @dev This function may point to any BytecodeStorageReader-compatible
630
+ * on-chain asset, including a web3call contract.
627
631
  * @param _projectId Project to be updated.
628
632
  * @param _assetAddress Address of the BytecodeStorageReader-compatible on-chain asset.
629
633
  */
@@ -2289,9 +2293,11 @@ contract GenArt721CoreV3_Engine_Flex is
2289
2293
 
2290
2294
  /**
2291
2295
  * @notice Returns external asset dependency for project `_projectId` at index `_index`.
2292
- * If the dependencyType is ONCHAIN, the `data` field will contain the extrated bytecode data and `cid`
2296
+ * If the dependencyType is ONCHAIN, the `data` field will contain the read BytecodeStorage data and `cid`
2293
2297
  * will be an empty string. Conversly, for any other dependencyType, the `data` field will be an empty string
2294
2298
  * and the `bytecodeAddress` will point to the zero address.
2299
+ * @dev If an ONCHAIN dependency is a web3call, the `data` field will be returned per the BytecodeStorageReader
2300
+ * contract's active spec, which at the time of this writing is the special string "#web3call_contract#".
2295
2301
  * If the dependencyType is ART_BLOCKS_DEPENDENCY_REGISTRY, the `cid` field will contain the string
2296
2302
  * representation of the dependencyNameAndVersion bytes32 value stored in the dependency registry (
2297
2303
  * at public address `artblocksDependencyRegistryAddress`)
@@ -9,11 +9,14 @@ import "../interfaces/v0.8.x/IGenArt721CoreTokenHashProviderV1.sol";
9
9
  import "../interfaces/v0.8.x/IGenArt721GeneratorV0.sol";
10
10
  import {IGenArt721CoreContractV3_Engine_Flex} from "../interfaces/v0.8.x/IGenArt721CoreContractV3_Engine_Flex.sol";
11
11
  import {IUniversalBytecodeStorageReader} from "../interfaces/v0.8.x/IUniversalBytecodeStorageReader.sol";
12
+ import {IWeb3Call} from "../interfaces/v0.8.x/IWeb3Call.sol";
13
+
12
14
  import "../libs/v0.8.x/Bytes32Strings.sol";
13
15
  import {JsonStatic} from "../libs/v0.8.x/JsonStatic.sol";
14
16
  import {ABHelpers} from "../libs/v0.8.x/ABHelpers.sol";
15
17
  import {AddressChunks} from "./AddressChunks.sol";
16
18
 
19
+ import {Base64} from "@openzeppelin-5.0/contracts/utils/Base64.sol";
17
20
  import "@openzeppelin-4.7/contracts/utils/Strings.sol";
18
21
  import "@openzeppelin-4.8/contracts-upgradeable/proxy/utils/Initializable.sol";
19
22
 
@@ -562,12 +565,12 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
562
565
  * as well as any dependency name and version if the dependency is an
563
566
  * ART_BLOCKS_DEPENDENCY_REGISTRY dependency.
564
567
  * @param coreContract The core contract address the project belongs to.
565
- * @param projectId The ID of the project to retrieve the external asset dependency for.
568
+ * @param tokenId The ID of the token to retrieve the external asset dependency for.
566
569
  * @param index The index of the external asset dependency to retrieve.
567
570
  */
568
571
  function _getExternalAssetDependencyKeysAndValues(
569
572
  address coreContract,
570
- uint256 projectId,
573
+ uint256 tokenId,
571
574
  uint256 index
572
575
  )
573
576
  internal
@@ -578,6 +581,8 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
578
581
  bytes32 /*dependencyNameAndVersion*/
579
582
  )
580
583
  {
584
+ // get project id from token id
585
+ uint256 projectId = ABHelpers.tokenIdToProjectId(tokenId);
581
586
  // default as not an art blocks dependency registry dependency
582
587
  bytes32 dependencyNameAndVersion; // default as not an art blocks dependency registry dependency
583
588
  // each external asset dependency has 3 fields, so pre-allocate array to avoid dynamic memory allocation
@@ -612,10 +617,33 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
612
617
  .ExternalAssetDependencyType
613
618
  .ONCHAIN
614
619
  ) {
615
- dependencyValues[2] = JsonStatic.newStringElement({
616
- value: externalAssetDependencyWithData.data, // assign data for ONCHAIN dependencies
617
- stringEncodingFlag: JsonStatic.StringEncodingFlag.BASE64
618
- });
620
+ if (
621
+ keccak256(
622
+ abi.encodePacked(externalAssetDependencyWithData.data)
623
+ ) == keccak256(abi.encodePacked("#web3call_contract#")) // @dev special string indicating a web3call contract
624
+ ) {
625
+ // treat as a web3call contract, inject token parameters based on the web3call contract
626
+ // @dev prepend with "#web3call#" to indicate that the data should be decoded as a web3call
627
+ // with base64 encoded key/value pairs
628
+ dependencyValues[2] = JsonStatic.newStringElement({
629
+ value: string.concat(
630
+ "#web3call#",
631
+ _getWeb3CallParameters({
632
+ web3callContract: externalAssetDependencyWithData
633
+ .bytecodeAddress,
634
+ coreContract: coreContract,
635
+ tokenId: tokenId
636
+ })
637
+ ),
638
+ stringEncodingFlag: JsonStatic.StringEncodingFlag.NONE // we base64 encode everything in the _getWeb3CallParameters function
639
+ });
640
+ } else {
641
+ // @dev typical ONCHAIN dependency, assign data for ONCHAIN dependencies
642
+ dependencyValues[2] = JsonStatic.newStringElement({
643
+ value: externalAssetDependencyWithData.data,
644
+ stringEncodingFlag: JsonStatic.StringEncodingFlag.BASE64
645
+ });
646
+ }
619
647
  } else {
620
648
  dependencyValues[2] = JsonStatic.newStringElement({
621
649
  value: "", // empty string for non-ONCHAIN dependencies
@@ -744,7 +772,7 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
744
772
  bytes32 dependencyNameAndVersion
745
773
  ) = _getExternalAssetDependencyKeysAndValues({
746
774
  coreContract: coreContract,
747
- projectId: projectId,
775
+ tokenId: tokenId,
748
776
  index: i
749
777
  });
750
778
  // handle ART_BLOCKS_DEPENDENCY_REGISTRY dependency type
@@ -870,7 +898,7 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
870
898
  headTags[1].tagContent = abi.encodePacked(
871
899
  "let tokenData = JSON.parse(`",
872
900
  tokenDataJson,
873
- '`, (key, value) => key === "data" && value !== null ? atob(value) : value);'
901
+ '`, (key, value) => key === "data" && value !== null ? value.startsWith("#web3call#") ? Object.entries(JSON.parse(atob(value.slice(10)))).reduce((acc, [k, v]) => ((acc[atob(k)] = atob(v)), acc), {}) : atob(value) : value);'
874
902
  );
875
903
  headTags[1].tagType = HTMLTagType.script;
876
904
  for (
@@ -997,6 +1025,47 @@ contract GenArt721GeneratorV0 is Initializable, IGenArt721GeneratorV0 {
997
1025
  .getDependencyDetails(dependencyNameAndVersion);
998
1026
  }
999
1027
 
1028
+ /**
1029
+ * @dev Helper function to get the web3call parameters for a given token.
1030
+ * Assumes the contract supports the IWeb3Call interface.
1031
+ * @param web3callContract The address of the contract to call.
1032
+ * @param coreContract The address of the core contract.
1033
+ * @param tokenId The ID of the token.
1034
+ * @return The web3call parameters for the token as a json string, keys and values encoded
1035
+ * as base64 strings, then the entire json string is encoded as base64.
1036
+ */
1037
+ function _getWeb3CallParameters(
1038
+ address web3callContract,
1039
+ address coreContract,
1040
+ uint256 tokenId
1041
+ ) internal view returns (string memory) {
1042
+ // get the onchain data for the contract
1043
+ IWeb3Call.TokenParam[] memory tokenParams = IWeb3Call(web3callContract)
1044
+ .getTokenParams({coreContract: coreContract, tokenId: tokenId});
1045
+ uint256 tokenParamsLength = tokenParams.length;
1046
+ // build key/value arrays for the token params
1047
+ string[] memory keys = JsonStatic.newKeysArray(tokenParamsLength);
1048
+ JsonStatic.Json[] memory values = JsonStatic.newValuesArray(
1049
+ tokenParamsLength
1050
+ );
1051
+ for (uint256 i = 0; i < tokenParamsLength; i++) {
1052
+ // @dev encode the key here as base64 string, to handle any special characters
1053
+ IWeb3Call.TokenParam memory tokenParam = tokenParams[i];
1054
+ keys[i] = Base64.encode(bytes(tokenParam.key));
1055
+ values[i] = JsonStatic.newStringElement({
1056
+ value: (tokenParam.value),
1057
+ stringEncodingFlag: JsonStatic.StringEncodingFlag.BASE64
1058
+ });
1059
+ }
1060
+ // use JsonStatic to build a printable json object from the key/value arrays
1061
+ JsonStatic.Json memory tokenParamsJson = JsonStatic.newObjectElement({
1062
+ keys: keys,
1063
+ values: values
1064
+ });
1065
+ // @dev base64 encode the entire json object for consistent encoding of data field
1066
+ return Base64.encode(bytes(tokenParamsJson.write()));
1067
+ }
1068
+
1000
1069
  /**
1001
1070
  * @dev Helper function to get a canvas tag or an empty tag depending on the dependency name.
1002
1071
  * @return The canvas tag or an empty tag.