@zoralabs/protocol-deployments 0.0.3 → 0.0.5-prerelease.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 (34) hide show
  1. package/.turbo/turbo-build.log +13 -13
  2. package/CHANGELOG.md +13 -0
  3. package/addresses/7777777.json +3 -3
  4. package/addresses/999.json +6 -6
  5. package/dist/index.cjs +76 -71
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.js +76 -68
  8. package/dist/index.js.map +1 -1
  9. package/dist/package/batchPublish.test.d.ts +16 -16
  10. package/dist/package/wagmiGenerated.d.ts +312 -201
  11. package/dist/package/wagmiGenerated.d.ts.map +1 -1
  12. package/package/wagmiGenerated.ts +76 -105
  13. package/package.json +2 -3
  14. package/script/CalculateDeterministicParams.s.sol +3 -3
  15. package/script/DeployMintersAndImplementations.s.sol +3 -3
  16. package/script/DeployNew1155Impl.s.sol +25 -0
  17. package/script/DeployNewImplementation.s.sol +3 -3
  18. package/script/DeployPreminterImpl.s.sol +3 -3
  19. package/script/DeployProxiesToNewChain.s.sol +4 -4
  20. package/script/DeployUpgradeGate.s.sol +5 -5
  21. package/script/Simulate1155Upgrade.s.sol +37 -0
  22. package/script/Upgrade.s.sol +3 -3
  23. package/script/UpgradePreminter.s.sol +3 -3
  24. package/src/DeploymentConfig.sol +124 -0
  25. package/src/DeploymentTestingUtils.sol +56 -0
  26. package/src/DeterministicDeployerScript.sol +253 -0
  27. package/src/DeterministicProxyDeployer.sol +139 -0
  28. package/src/IImmutableCreate2Factory.sol +59 -0
  29. package/src/ZoraDeployerBase.sol +144 -0
  30. package/src/ZoraDeployerUtils.sol +199 -0
  31. package/test/NewFactoryProxyDeployer.t.sol +4 -4
  32. package/test/ZoraCreator1155Factory_Fork.t.sol +1 -3
  33. package/test/ZoraCreator1155PremintExecutorForkTest.t.sol +1 -1
  34. package/wagmi.config.ts +1 -7
@@ -0,0 +1,124 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.17;
3
+
4
+ import "forge-std/Test.sol";
5
+ import {CommonBase} from "forge-std/Base.sol";
6
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
7
+ import {Script} from "forge-std/Script.sol";
8
+
9
+ /// @notice Chain configuration for constants set manually during deploy. Does not get written to after deploys.
10
+ struct ChainConfig {
11
+ /// @notice The user that owns the factory proxy. Allows ability to upgrade for new implementations deployed.
12
+ address factoryOwner;
13
+ /// @notice Mint fee recipient user
14
+ address mintFeeRecipient;
15
+ /// @notice Protocol rewards contract address
16
+ address protocolRewards;
17
+ }
18
+
19
+ /// @notice Deployment addresses – set to new deployed addresses by the scripts.
20
+ struct Deployment {
21
+ /// @notice Fixed price minter strategy configuration contract
22
+ address fixedPriceSaleStrategy;
23
+ /// @notice Merkle minter strategy (formerly presale) configuration
24
+ address merkleMintSaleStrategy;
25
+ /// @notice Redeem minter factory contract for redeem sales configurations
26
+ address redeemMinterFactory;
27
+ /// @notice Implementation contract for the 1155 contract
28
+ address contract1155Impl;
29
+ /// @notice Implementation contract version for the 1155 contract
30
+ string contract1155ImplVersion;
31
+ /// @notice Factory implementation contract that is the impl for the above proxy.
32
+ address factoryImpl;
33
+ /// @notice Factory proxy contract that creates zora drops style NFT contracts
34
+ address factoryProxy;
35
+ /// @notice Preminter proxy contract address
36
+ address preminterImpl;
37
+ /// @notice Preminter implementation contract address
38
+ address preminterProxy;
39
+ /// @notice Upgrade gate
40
+ address upgradeGate;
41
+ }
42
+
43
+ abstract contract DeploymentConfig is Script {
44
+ using stdJson for string;
45
+
46
+ /// @notice ChainID convenience getter
47
+ /// @return id chainId
48
+ function chainId() internal view virtual returns (uint256 id);
49
+
50
+ ///
51
+ // These are the JSON key constants to standardize writing and reading configuration
52
+ ///
53
+
54
+ string constant FACTORY_OWNER = "FACTORY_OWNER";
55
+ string constant MINT_FEE_RECIPIENT = "MINT_FEE_RECIPIENT";
56
+ string constant PROTOCOL_REWARDS = "PROTOCOL_REWARDS";
57
+
58
+ string constant FIXED_PRICE_SALE_STRATEGY = "FIXED_PRICE_SALE_STRATEGY";
59
+ string constant MERKLE_MINT_SALE_STRATEGY = "MERKLE_MINT_SALE_STRATEGY";
60
+ string constant REDEEM_MINTER_FACTORY = "REDEEM_MINTER_FACTORY";
61
+ string constant CONTRACT_1155_IMPL = "CONTRACT_1155_IMPL";
62
+ string constant CONTRACT_1155_IMPL_VERSION = "CONTRACT_1155_IMPL_VERSION";
63
+ string constant FACTORY_IMPL = "FACTORY_IMPL";
64
+ string constant FACTORY_PROXY = "FACTORY_PROXY";
65
+ string constant PREMINTER_PROXY = "PREMINTER_PROXY";
66
+ string constant PREMINTER_IMPL = "PREMINTER_IMPL";
67
+ string constant UPGRADE_GATE = "UPGRADE_GATE";
68
+
69
+ /// @notice Return a prefixed key for reading with a ".".
70
+ /// @param key key to prefix
71
+ /// @return prefixed key
72
+ function getKeyPrefix(string memory key) internal pure returns (string memory) {
73
+ return string.concat(".", key);
74
+ }
75
+
76
+ /// @notice Returns the chain configuration struct from the JSON configuration file
77
+ /// @return chainConfig structure
78
+ function getChainConfig() internal view returns (ChainConfig memory chainConfig) {
79
+ string memory json = vm.readFile(string.concat("chainConfigs/", Strings.toString(chainId()), ".json"));
80
+ chainConfig.factoryOwner = json.readAddress(getKeyPrefix(FACTORY_OWNER));
81
+ chainConfig.mintFeeRecipient = json.readAddress(getKeyPrefix(MINT_FEE_RECIPIENT));
82
+ chainConfig.protocolRewards = json.readAddress(getKeyPrefix(PROTOCOL_REWARDS));
83
+ }
84
+
85
+ function readAddressOrDefaultToZero(string memory json, string memory key) internal view returns (address addr) {
86
+ string memory keyPrefix = getKeyPrefix(key);
87
+
88
+ if (vm.keyExists(json, keyPrefix)) {
89
+ addr = json.readAddress(keyPrefix);
90
+ } else {
91
+ addr = address(0);
92
+ }
93
+ }
94
+
95
+ /// @notice Get the deployment configuration struct from the JSON configuration file
96
+ /// @return deployment deployment configuration structure
97
+ function getDeployment() internal view returns (Deployment memory deployment) {
98
+ string memory json = vm.readFile(string.concat("addresses/", Strings.toString(chainId()), ".json"));
99
+ deployment.fixedPriceSaleStrategy = readAddressOrDefaultToZero(json, FIXED_PRICE_SALE_STRATEGY);
100
+ deployment.merkleMintSaleStrategy = readAddressOrDefaultToZero(json, MERKLE_MINT_SALE_STRATEGY);
101
+ deployment.redeemMinterFactory = readAddressOrDefaultToZero(json, REDEEM_MINTER_FACTORY);
102
+ deployment.contract1155Impl = readAddressOrDefaultToZero(json, CONTRACT_1155_IMPL);
103
+ deployment.contract1155ImplVersion = json.readString(getKeyPrefix(CONTRACT_1155_IMPL_VERSION));
104
+ deployment.factoryImpl = readAddressOrDefaultToZero(json, FACTORY_IMPL);
105
+ deployment.factoryProxy = readAddressOrDefaultToZero(json, FACTORY_PROXY);
106
+ deployment.preminterImpl = readAddressOrDefaultToZero(json, PREMINTER_IMPL);
107
+ deployment.preminterProxy = readAddressOrDefaultToZero(json, PREMINTER_PROXY);
108
+ deployment.upgradeGate = readAddressOrDefaultToZero(json, UPGRADE_GATE);
109
+ }
110
+ }
111
+
112
+ contract ForkDeploymentConfig is DeploymentConfig {
113
+ function chainId() internal view override returns (uint256 id) {
114
+ return block.chainid;
115
+ }
116
+ }
117
+
118
+ contract ScriptDeploymentConfig is DeploymentConfig {
119
+ function chainId() internal view override returns (uint256 id) {
120
+ assembly {
121
+ id := chainid()
122
+ }
123
+ }
124
+ }
@@ -0,0 +1,56 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import "forge-std/Script.sol";
5
+ import {IMinter1155} from "@zoralabs/zora-1155-contracts/src/interfaces/IMinter1155.sol";
6
+ import {ZoraCreator1155PremintExecutorImpl} from "@zoralabs/zora-1155-contracts/src/delegation/ZoraCreator1155PremintExecutorImpl.sol";
7
+ import {ZoraCreator1155FactoryImpl} from "@zoralabs/zora-1155-contracts/src/factory/ZoraCreator1155FactoryImpl.sol";
8
+ import {ZoraCreator1155Attribution, ContractCreationConfig, PremintConfig, TokenCreationConfig} from "@zoralabs/zora-1155-contracts/src/delegation/ZoraCreator1155Attribution.sol";
9
+ import {ZoraCreator1155Impl} from "@zoralabs/zora-1155-contracts/src/nft/ZoraCreator1155Impl.sol";
10
+
11
+ contract DeploymentTestingUtils is Script {
12
+ function signAndExecutePremint(address premintExecutorProxyAddress) internal {
13
+ console2.log("preminter proxy", premintExecutorProxyAddress);
14
+
15
+ (address creator, uint256 creatorPrivateKey) = makeAddrAndKey("creator");
16
+ ZoraCreator1155PremintExecutorImpl preminterAtProxy = ZoraCreator1155PremintExecutorImpl(premintExecutorProxyAddress);
17
+
18
+ IMinter1155 fixedPriceMinter = ZoraCreator1155FactoryImpl(address(preminterAtProxy.zora1155Factory())).fixedPriceMinter();
19
+
20
+ PremintConfig memory premintConfig = PremintConfig({
21
+ tokenConfig: TokenCreationConfig({
22
+ tokenURI: "blah.token",
23
+ maxSupply: 10,
24
+ maxTokensPerAddress: 5,
25
+ pricePerToken: 0,
26
+ mintStart: 0,
27
+ mintDuration: 0,
28
+ royaltyMintSchedule: 0,
29
+ royaltyBPS: 100,
30
+ royaltyRecipient: creator,
31
+ fixedPriceMinter: address(fixedPriceMinter)
32
+ }),
33
+ uid: 100,
34
+ version: 0,
35
+ deleted: false
36
+ });
37
+
38
+ // now interface with proxy preminter - sign and execute the premint
39
+ ContractCreationConfig memory contractConfig = ContractCreationConfig({contractAdmin: creator, contractName: "blah", contractURI: "blah.contract"});
40
+ address deterministicAddress = preminterAtProxy.getContractAddress(contractConfig);
41
+
42
+ // sign the premint
43
+ bytes32 digest = ZoraCreator1155Attribution.premintHashedTypeDataV4(premintConfig, deterministicAddress, block.chainid);
44
+
45
+ (uint8 v, bytes32 r, bytes32 s) = vm.sign(creatorPrivateKey, digest);
46
+
47
+ uint256 quantityToMint = 1;
48
+
49
+ bytes memory signature = abi.encodePacked(r, s, v);
50
+
51
+ // execute the premint
52
+ uint256 tokenId = preminterAtProxy.premint{value: 0.000777 ether}(contractConfig, premintConfig, signature, quantityToMint, "");
53
+
54
+ require(ZoraCreator1155Impl(deterministicAddress).delegatedTokenId(premintConfig.uid) == tokenId, "token id not created for uid");
55
+ }
56
+ }
@@ -0,0 +1,253 @@
1
+ // spdx-license-identifier: mit
2
+ pragma solidity ^0.8.17;
3
+
4
+ import "forge-std/Script.sol";
5
+ import {Deployment, ChainConfig} from "./DeploymentConfig.sol";
6
+ import {ProxyShim} from "@zoralabs/zora-1155-contracts/src/utils/ProxyShim.sol";
7
+ import {DeterministicProxyDeployer} from "./DeterministicProxyDeployer.sol";
8
+ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
9
+ import {UpgradeGate} from "@zoralabs/zora-1155-contracts/src/upgrades/UpgradeGate.sol";
10
+ import {LibString} from "solady/utils/LibString.sol";
11
+ import {ZoraDeployerUtils} from "./ZoraDeployerUtils.sol";
12
+
13
+ struct DeterministicParams {
14
+ bytes proxyDeployerCreationCode;
15
+ bytes proxyCreationCode;
16
+ address deployerAddress;
17
+ address proxyDeployerAddress;
18
+ bytes32 proxyDeployerSalt;
19
+ bytes32 proxyShimSalt;
20
+ bytes32 proxySalt;
21
+ address deterministicProxyAddress;
22
+ }
23
+
24
+ contract DeterministicDeployerScript is Script {
25
+ using stdJson for string;
26
+
27
+ // copied from: https://github.com/karmacoma-eth/foundry-playground/blob/main/script/MineSaltScript.sol#L17C1-L36C9
28
+ function mineSalt(address deployer, bytes32 initCodeHash, string memory startsWith) internal returns (bytes32 salt, address expectedAddress) {
29
+ string[] memory args = new string[](8);
30
+ args[0] = "cast";
31
+ args[1] = "create2";
32
+ args[2] = "--starts-with";
33
+ args[3] = startsWith;
34
+ args[4] = "--deployer";
35
+ args[5] = LibString.toHexString(deployer);
36
+ args[6] = "--init-code-hash";
37
+ args[7] = LibString.toHexStringNoPrefix(uint256(initCodeHash), 32);
38
+ string memory result = string(vm.ffi(args));
39
+
40
+ uint256 addressIndex = LibString.indexOf(result, "Address: ");
41
+ string memory addressStr = LibString.slice(result, addressIndex + 9, addressIndex + 9 + 42);
42
+ expectedAddress = vm.parseAddress(addressStr);
43
+
44
+ uint256 saltIndex = LibString.indexOf(result, "Salt: ");
45
+ string memory saltStr = LibString.slice(result, saltIndex + 6, bytes(result).length);
46
+
47
+ salt = bytes32(vm.parseUint(saltStr));
48
+ }
49
+
50
+ function saltWithAddressInFirst20Bytes(address addressToMakeSaltWith, uint256 suffix) internal pure returns (bytes32) {
51
+ uint256 shifted = uint256(uint160(address(addressToMakeSaltWith))) << 96;
52
+
53
+ // shifted on the left, suffix on the right:
54
+
55
+ return bytes32(shifted | suffix);
56
+ }
57
+
58
+ function paramsFilePath(string memory proxyName) internal pure returns (string memory) {
59
+ return string.concat("deterministicConfig/", proxyName, "/params.json");
60
+ }
61
+
62
+ function signaturesFilePath(string memory proxyName) internal pure returns (string memory) {
63
+ return string.concat("deterministicConfig/", proxyName, "/signatures.json");
64
+ }
65
+
66
+ function serializeAndSaveOutput(DeterministicParams memory params, string memory proxyName) internal {
67
+ string memory result = "deterministicKey";
68
+
69
+ vm.serializeBytes(result, "proxyDeployerCreationCode", params.proxyDeployerCreationCode);
70
+ vm.serializeBytes(result, "proxyCreationCode", params.proxyCreationCode);
71
+ vm.serializeAddress(result, "deployerAddress", params.deployerAddress);
72
+ vm.serializeAddress(result, "proxyDeployerAddress", params.proxyDeployerAddress);
73
+ vm.serializeBytes32(result, "proxyDeployerSalt", params.proxyDeployerSalt);
74
+ vm.serializeBytes32(result, "proxyShimSalt", params.proxyShimSalt);
75
+ vm.serializeBytes32(result, "proxySalt", params.proxySalt);
76
+
77
+ string memory finalOutput = vm.serializeAddress(result, "deterministicProxyAddress", params.deterministicProxyAddress);
78
+
79
+ console2.log(finalOutput);
80
+
81
+ vm.writeJson(finalOutput, paramsFilePath(proxyName));
82
+ }
83
+
84
+ function readDeterministicParams(string memory proxyName) internal view returns (DeterministicParams memory params) {
85
+ string memory deployConfig = vm.readFile(paramsFilePath(proxyName));
86
+
87
+ return
88
+ DeterministicParams({
89
+ proxyDeployerCreationCode: deployConfig.readBytes(".proxyDeployerCreationCode"),
90
+ proxyCreationCode: deployConfig.readBytes(".proxyCreationCode"),
91
+ deployerAddress: deployConfig.readAddress(".deployerAddress"),
92
+ proxyDeployerAddress: deployConfig.readAddress(".proxyDeployerAddress"),
93
+ proxyDeployerSalt: deployConfig.readBytes32(".proxyDeployerSalt"),
94
+ proxyShimSalt: deployConfig.readBytes32(".proxyShimSalt"),
95
+ proxySalt: deployConfig.readBytes32(".proxySalt"),
96
+ deterministicProxyAddress: deployConfig.readAddress(".deterministicProxyAddress")
97
+ });
98
+ }
99
+
100
+ function readDeterministicParamsAndSig(
101
+ string memory proxyName,
102
+ uint256 chain
103
+ ) internal view returns (DeterministicParams memory params, bytes memory signature) {
104
+ string memory signatures = vm.readFile(signaturesFilePath(proxyName));
105
+
106
+ params = readDeterministicParams(proxyName);
107
+
108
+ signature = signatures.readBytes(string.concat(".", string.concat(vm.toString(chain))));
109
+ }
110
+
111
+ function getProxyDeployerParams() internal view returns (bytes32 proxyDeployerSalt, bytes memory proxyDeployerCreationCode, address proxyDeployerAddress) {
112
+ proxyDeployerSalt = ZoraDeployerUtils.FACTORY_DEPLOYER_DEPLOYMENT_SALT;
113
+
114
+ proxyDeployerCreationCode = type(DeterministicProxyDeployer).creationCode;
115
+
116
+ // we can know deterministically what the address of the new factory proxy deployer will be, given it's deployed from with the salt and init code,
117
+ // from the ImmutableCreate2Factory
118
+ proxyDeployerAddress = ZoraDeployerUtils.IMMUTABLE_CREATE2_FACTORY.findCreate2Address(proxyDeployerSalt, proxyDeployerCreationCode);
119
+ }
120
+
121
+ function getProxyShimParams(
122
+ address proxyDeployerAddress,
123
+ address deployerAddress,
124
+ uint256 proxyShimSaltSuffix
125
+ ) internal pure returns (bytes memory proxyShimInitCode, bytes32 proxyShimSalt, address proxyShimAddress) {
126
+ proxyShimInitCode = abi.encodePacked(type(ProxyShim).creationCode, abi.encode(proxyDeployerAddress));
127
+
128
+ // create any arbitrary salt for proxy shim (this can be anything, we just care about the resulting address)
129
+ proxyShimSalt = saltWithAddressInFirst20Bytes(deployerAddress, proxyShimSaltSuffix);
130
+
131
+ // now get deterministic proxy shim address based on salt, deployer address, which will be DeterministicProxyDeployer address and init code
132
+ proxyShimAddress = Create2.computeAddress(proxyShimSalt, keccak256(proxyShimInitCode), proxyDeployerAddress);
133
+ }
134
+
135
+ function getProxyParams(
136
+ bytes memory proxyCreationCode,
137
+ address proxyShimAddress,
138
+ address proxyDeployerAddress
139
+ ) internal returns (bytes32 proxySalt, address deterministicProxyAddress) {
140
+ bytes memory factoryProxyInitCode = abi.encodePacked(proxyCreationCode, abi.encode(proxyShimAddress, ""));
141
+ bytes32 creationCodeHash = keccak256(factoryProxyInitCode);
142
+
143
+ (proxySalt, deterministicProxyAddress) = mineSalt(proxyDeployerAddress, creationCodeHash, "777777");
144
+ }
145
+
146
+ function getDeterministicDeploymentParams(
147
+ address deployerAddress,
148
+ bytes memory proxyCreationCode,
149
+ uint256 proxyShimSaltSuffix
150
+ ) internal returns (DeterministicParams memory) {
151
+ // 1. Get salt with first bytes that match address, and resulting determinisitic factory proxy deployer address
152
+ (bytes32 proxyDeployerSalt, bytes memory proxyDeployerCreationCode, address proxyDeployerAddress) = getProxyDeployerParams();
153
+ // replace first 20 characters of salt with deployer address, so that the salt can be used with
154
+ // ImmutableCreate2Factory.safeCreate2 when called by this deployer's account:
155
+
156
+ // 2. Get random proxy shim salt, and resulting deterministic address
157
+ // Proxy shim will be initialized with the factory deployer address as the owner, allowing only the factory deployer to upgrade the proxy,
158
+ // to the eventual factory implementation
159
+ (, bytes32 proxyShimSalt, address proxyShimAddress) = getProxyShimParams({
160
+ proxyDeployerAddress: proxyDeployerAddress,
161
+ deployerAddress: deployerAddress,
162
+ proxyShimSaltSuffix: proxyShimSaltSuffix
163
+ });
164
+
165
+ // 3. Mine for a salt that can be used to deterministically create the factory proxy, given the proxy shim address, which is passed as the
166
+ // constructor argument, and the deployer, which is the new factory proxy deployer, which we know the address of deterministically
167
+ (bytes32 proxySalt, address deterministicProxyAddress) = getProxyParams({
168
+ proxyCreationCode: proxyCreationCode,
169
+ proxyShimAddress: proxyShimAddress,
170
+ proxyDeployerAddress: proxyDeployerAddress
171
+ });
172
+
173
+ return
174
+ DeterministicParams({
175
+ proxyDeployerCreationCode: proxyDeployerCreationCode,
176
+ proxyCreationCode: proxyCreationCode,
177
+ deployerAddress: deployerAddress,
178
+ proxyDeployerAddress: proxyDeployerAddress,
179
+ proxyDeployerSalt: proxyDeployerSalt,
180
+ proxyShimSalt: proxyShimSalt,
181
+ proxySalt: proxySalt,
182
+ deterministicProxyAddress: deterministicProxyAddress
183
+ });
184
+ }
185
+
186
+ error MismatchedAddress(address expected, address actual);
187
+
188
+ function readFactoryProxyDeterminsticParams() internal view returns (DeterministicParams memory) {
189
+ return readDeterministicParams("factoryProxy");
190
+ }
191
+
192
+ function readPreminterProxyDeterminsticParams() internal view returns (DeterministicParams memory) {
193
+ return readDeterministicParams("premintExecutorProxy");
194
+ }
195
+
196
+ function getOrCreateProxyDeployer() internal returns (DeterministicProxyDeployer factoryDeployer) {
197
+ DeterministicParams memory params = readFactoryProxyDeterminsticParams();
198
+ address proxyDeployerAddress = params.proxyDeployerAddress;
199
+ bytes32 proxyDeployerSalt = params.proxyDeployerSalt;
200
+ bytes memory proxyDeployerCreationCode = params.proxyDeployerCreationCode;
201
+
202
+ if (ZoraDeployerUtils.IMMUTABLE_CREATE2_FACTORY.hasBeenDeployed(proxyDeployerAddress)) {
203
+ factoryDeployer = DeterministicProxyDeployer(proxyDeployerAddress);
204
+ } else {
205
+ factoryDeployer = DeterministicProxyDeployer(ZoraDeployerUtils.IMMUTABLE_CREATE2_FACTORY.safeCreate2(proxyDeployerSalt, proxyDeployerCreationCode));
206
+ }
207
+
208
+ if (address(factoryDeployer) != params.proxyDeployerAddress) revert MismatchedAddress(params.proxyDeployerAddress, address(factoryDeployer));
209
+ }
210
+
211
+ function deployDeterministicProxy(string memory proxyName, address implementation, address owner, uint256 chain) internal returns (address) {
212
+ (DeterministicParams memory params, bytes memory signature) = readDeterministicParamsAndSig(proxyName, chain);
213
+
214
+ DeterministicProxyDeployer factoryDeployer = getOrCreateProxyDeployer();
215
+
216
+ address resultAddress = factoryDeployer.createFactoryProxyDeterministic(
217
+ params.proxyShimSalt,
218
+ params.proxySalt,
219
+ params.proxyCreationCode,
220
+ params.deterministicProxyAddress,
221
+ implementation,
222
+ owner,
223
+ signature
224
+ );
225
+
226
+ require(resultAddress == params.deterministicProxyAddress, "DeterministicDeployerScript: proxy address mismatch");
227
+
228
+ return resultAddress;
229
+ }
230
+
231
+ function deployUpgradeGate(uint256 chain, address upgradeGateOwner) internal returns (address) {
232
+ string memory signatures = vm.readFile(signaturesFilePath("upgradeGate"));
233
+ bytes memory signature = signatures.readBytes(string.concat(".", string.concat(vm.toString(chain))));
234
+
235
+ string memory upgradeGateParams = vm.readFile("./deterministicConfig/upgradeGate/params.json");
236
+
237
+ bytes32 genericCreationSalt = vm.parseJsonBytes32(upgradeGateParams, ".salt");
238
+ bytes memory creationCode = vm.parseJsonBytes(upgradeGateParams, ".creationCode");
239
+
240
+ DeterministicProxyDeployer factoryDeployer = getOrCreateProxyDeployer();
241
+
242
+ address resultAddress = factoryDeployer.createAndInitGenericContractDeterministic({
243
+ genericCreationSalt: genericCreationSalt,
244
+ creationCode: creationCode,
245
+ initCall: abi.encodeWithSelector(UpgradeGate.initialize.selector, upgradeGateOwner),
246
+ signature: signature
247
+ });
248
+
249
+ require(resultAddress == vm.parseJsonAddress(upgradeGateParams, ".upgradeGateAddress"), "DeterministicDeployerScript: upgrade gate address mismatch");
250
+
251
+ return resultAddress;
252
+ }
253
+ }
@@ -0,0 +1,139 @@
1
+ // spdx-license-identifier: mit
2
+ pragma solidity ^0.8.17;
3
+
4
+ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
5
+ import {ProxyShim} from "@zoralabs/zora-1155-contracts/src/utils/ProxyShim.sol";
6
+ import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
7
+ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
8
+
9
+ interface IUpgradeableProxy {
10
+ function upgradeTo(address newImplementation) external;
11
+
12
+ function initialize(address newOwner) external;
13
+ }
14
+
15
+ /// @notice Deterministic proxy deployer for deploying zora 1155 contract suite
16
+ /// @dev Supports both generic contracts and the UUPS shim proxy pattern used for ZORA 1155
17
+ /// @dev Requires approved signatures depending on the salt hashes (pattern from IMMUTABLE_CREATE2_FACTORY)
18
+ contract DeterministicProxyDeployer is EIP712 {
19
+ error FactoryProxyAddressMismatch(address expected, address actual);
20
+ error FailedToInitGenericDeployedContract();
21
+
22
+ constructor() EIP712("DeterministicProxyDeployer", "1") {}
23
+
24
+ /// Creates a new factory proxy at a Deterministic address, with this address as the owner
25
+ /// Upgrades the proxy to the factory implementation, and sets the new owner as the owner.
26
+ /// @param proxyShimSalt Salt for deterministic proxy shim address
27
+ /// @param factoryProxySalt Salt for deterministic factory proxy address
28
+ function _createAndInitializeNewFactoryProxyDeterministic(
29
+ bytes32 proxyShimSalt,
30
+ bytes32 factoryProxySalt,
31
+ bytes calldata proxyCreationCode,
32
+ address expectedFactoryProxyAddress,
33
+ address implementationAddress,
34
+ address newOwner
35
+ ) internal returns (address factoryProxyAddress) {
36
+ // create proxy shim and factory proxy deterministically
37
+ ProxyShim proxyShim = new ProxyShim{salt: proxyShimSalt}({_canUpgrade: address(this)});
38
+
39
+ bytes memory creationCode = abi.encodePacked(proxyCreationCode, abi.encode(address(proxyShim), ""));
40
+
41
+ address factoryProxy = Create2.deploy(0, factoryProxySalt, creationCode);
42
+
43
+ if (factoryProxy != expectedFactoryProxyAddress) {
44
+ revert FactoryProxyAddressMismatch(expectedFactoryProxyAddress, address(factoryProxy));
45
+ }
46
+
47
+ factoryProxyAddress = address(factoryProxy);
48
+ IUpgradeableProxy proxy = IUpgradeableProxy(address(factoryProxy));
49
+ proxy.upgradeTo(implementationAddress);
50
+ proxy.initialize(newOwner);
51
+
52
+ return factoryProxyAddress;
53
+ }
54
+
55
+ function _createAndInitGenericContractDeterministic(
56
+ bytes32 genericCreationSalt,
57
+ bytes calldata creationCode,
58
+ bytes calldata initCall
59
+ ) internal returns (address resultAddress) {
60
+ resultAddress = Create2.deploy(0, genericCreationSalt, creationCode);
61
+
62
+ (bool success, ) = resultAddress.call(initCall);
63
+ if (!success) {
64
+ revert FailedToInitGenericDeployedContract();
65
+ }
66
+ }
67
+
68
+ bytes32 constant DOMAIN_UPGRADEABLE_PROXY =
69
+ keccak256("createProxy(bytes32 proxyShimSalt,bytes32 proxySalt,bytes proxyCreationCode,address implementationAddress,address owner)");
70
+ bytes32 constant DOMAIN_GENERIC_CREATION = keccak256("createGenericContract(bytes32 salt,bytes creationCode,bytes initCall)");
71
+
72
+ function recoverSignature(bytes32 digest, bytes calldata signature) public pure returns (address) {
73
+ return ECDSA.recover(digest, signature);
74
+ }
75
+
76
+ function hashedDigestFactoryProxy(
77
+ bytes32 proxyShimSalt,
78
+ bytes32 proxySalt,
79
+ bytes calldata proxyCreationCode,
80
+ address implementationAddress,
81
+ address newOwner
82
+ ) public view returns (bytes32) {
83
+ return
84
+ _hashTypedDataV4(
85
+ keccak256(abi.encode(DOMAIN_UPGRADEABLE_PROXY, proxyShimSalt, proxySalt, keccak256(proxyCreationCode), implementationAddress, newOwner))
86
+ );
87
+ }
88
+
89
+ function hashedDigestGenericCreation(bytes32 salt, bytes calldata creationCode, bytes calldata initCall) public view returns (bytes32) {
90
+ return _hashTypedDataV4(keccak256(abi.encode(DOMAIN_GENERIC_CREATION, salt, keccak256(creationCode), keccak256(initCall))));
91
+ }
92
+
93
+ function requireContainsCaller(address caller, bytes32 salt) private pure {
94
+ // prevent contract submissions from being stolen from tx.pool by requiring
95
+ // that the first 20 bytes of the submitted salt match msg.sender.
96
+ require((address(bytes20(salt)) == caller) || (bytes20(salt) == bytes20(0)), "Invalid salt - first 20 bytes of the salt must match calling address.");
97
+ }
98
+
99
+ /// Creates a new factory proxy at a Deterministic address, with this address as the owner
100
+ /// Upgrades the proxy to the factory implementation, and sets the new owner as the owner.
101
+ /// @param proxyShimSalt Salt for deterministic proxy shim address
102
+ /// @param proxySalt Salt for deterministic factory proxy address
103
+ function createFactoryProxyDeterministic(
104
+ bytes32 proxyShimSalt,
105
+ bytes32 proxySalt,
106
+ bytes calldata proxyCreationCode,
107
+ address expectedFactoryProxyAddress,
108
+ address implementationAddress,
109
+ address owner,
110
+ bytes calldata signature
111
+ ) external returns (address factoryProxyAddress) {
112
+ address signer = recoverSignature(hashedDigestFactoryProxy(proxyShimSalt, proxySalt, proxyCreationCode, implementationAddress, owner), signature);
113
+
114
+ requireContainsCaller(signer, proxyShimSalt);
115
+
116
+ return
117
+ _createAndInitializeNewFactoryProxyDeterministic(
118
+ proxyShimSalt,
119
+ proxySalt,
120
+ proxyCreationCode,
121
+ expectedFactoryProxyAddress,
122
+ implementationAddress,
123
+ owner
124
+ );
125
+ }
126
+
127
+ function createAndInitGenericContractDeterministic(
128
+ bytes32 genericCreationSalt,
129
+ bytes calldata creationCode,
130
+ bytes calldata initCall,
131
+ bytes calldata signature
132
+ ) external returns (address resultAddress) {
133
+ address signer = recoverSignature(hashedDigestGenericCreation(genericCreationSalt, creationCode, initCall), signature);
134
+
135
+ requireContainsCaller(signer, genericCreationSalt);
136
+
137
+ return _createAndInitGenericContractDeterministic(genericCreationSalt, creationCode, initCall);
138
+ }
139
+ }
@@ -0,0 +1,59 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.17;
3
+
4
+ // Extracted interface of ImmutableCreate2Factory.sol, located at https://raw.githubusercontent.com/0age/metamorphic/master/contracts/ImmutableCreate2Factory.sol
5
+
6
+ interface IImmutableCreate2Factory {
7
+ /**
8
+ * @dev Create a contract using CREATE2 by submitting a given salt or nonce
9
+ * along with the initialization code for the contract. Note that the first 20
10
+ * bytes of the salt must match those of the calling address, which prevents
11
+ * contract creation events from being submitted by unintended parties.
12
+ * @param salt bytes32 The nonce that will be passed into the CREATE2 call.
13
+ * @param initializationCode bytes The initialization code that will be passed
14
+ * into the CREATE2 call.
15
+ * @return deploymentAddress Address of the contract that will be created, or the null address
16
+ * if a contract already exists at that address.
17
+ */
18
+ function safeCreate2(bytes32 salt, bytes calldata initializationCode) external payable returns (address deploymentAddress);
19
+
20
+ /**
21
+ * @dev Compute the address of the contract that will be created when
22
+ * submitting a given salt or nonce to the contract along with the contract's
23
+ * initialization code. The CREATE2 address is computed in accordance with
24
+ * EIP-1014, and adheres to the formula therein of
25
+ * `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]` when
26
+ * performing the computation. The computed address is then checked for any
27
+ * existing contract code - if so, the null address will be returned instead.
28
+ * @param salt bytes32 The nonce passed into the CREATE2 address calculation.
29
+ * @param initCode bytes The contract initialization code to be used.
30
+ * that will be passed into the CREATE2 address calculation.
31
+ * @return deploymentAddress Address of the contract that will be created, or the null address
32
+ * if a contract has already been deployed to that address.
33
+ */
34
+ function findCreate2Address(bytes32 salt, bytes calldata initCode) external view returns (address deploymentAddress);
35
+
36
+ /**
37
+ * @dev Compute the address of the contract that will be created when
38
+ * submitting a given salt or nonce to the contract along with the keccak256
39
+ * hash of the contract's initialization code. The CREATE2 address is computed
40
+ * in accordance with EIP-1014, and adheres to the formula therein of
41
+ * `keccak256( 0xff ++ address ++ salt ++ keccak256(init_code)))[12:]` when
42
+ * performing the computation. The computed address is then checked for any
43
+ * existing contract code - if so, the null address will be returned instead.
44
+ * @param salt bytes32 The nonce passed into the CREATE2 address calculation.
45
+ * @param initCodeHash bytes32 The keccak256 hash of the initialization code
46
+ * that will be passed into the CREATE2 address calculation.
47
+ * @return deploymentAddress Address of the contract that will be created, or the null address
48
+ * if a contract has already been deployed to that address.
49
+ */
50
+ function findCreate2AddressViaHash(bytes32 salt, bytes32 initCodeHash) external view returns (address deploymentAddress);
51
+
52
+ /**
53
+ * @dev Determine if a contract has already been deployed by the factory to a
54
+ * given address.
55
+ * @param deploymentAddress address The contract address to check.
56
+ * @return True if the contract has been deployed, false otherwise.
57
+ */
58
+ function hasBeenDeployed(address deploymentAddress) external view returns (bool);
59
+ }