@diamondslab/diamonds 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/README.md +618 -0
  2. package/diamonds/README.md +3 -0
  3. package/dist/core/CallbackManager.d.ts +13 -0
  4. package/dist/core/CallbackManager.d.ts.map +1 -0
  5. package/dist/core/CallbackManager.js +95 -0
  6. package/dist/core/CallbackManager.js.map +1 -0
  7. package/dist/core/DeploymentManager.d.ts +10 -0
  8. package/dist/core/DeploymentManager.d.ts.map +1 -0
  9. package/dist/core/DeploymentManager.js +50 -0
  10. package/dist/core/DeploymentManager.js.map +1 -0
  11. package/dist/core/Diamond.d.ts +58 -0
  12. package/dist/core/Diamond.d.ts.map +1 -0
  13. package/dist/core/Diamond.js +146 -0
  14. package/dist/core/Diamond.js.map +1 -0
  15. package/dist/core/DiamondDeployer.d.ts +10 -0
  16. package/dist/core/DiamondDeployer.d.ts.map +1 -0
  17. package/dist/core/DiamondDeployer.js +33 -0
  18. package/dist/core/DiamondDeployer.js.map +1 -0
  19. package/dist/core/index.d.ts +5 -0
  20. package/dist/core/index.d.ts.map +1 -0
  21. package/dist/core/index.js +12 -0
  22. package/dist/core/index.js.map +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +22 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/repositories/DBDeploymentRepository.d.ts +1 -0
  28. package/dist/repositories/DBDeploymentRepository.d.ts.map +1 -0
  29. package/dist/repositories/DBDeploymentRepository.js +20 -0
  30. package/dist/repositories/DBDeploymentRepository.js.map +1 -0
  31. package/dist/repositories/DeploymentRepository.d.ts +8 -0
  32. package/dist/repositories/DeploymentRepository.d.ts.map +1 -0
  33. package/dist/repositories/DeploymentRepository.js +7 -0
  34. package/dist/repositories/DeploymentRepository.js.map +1 -0
  35. package/dist/repositories/FileDeploymentRepository.d.ts +18 -0
  36. package/dist/repositories/FileDeploymentRepository.d.ts.map +1 -0
  37. package/dist/repositories/FileDeploymentRepository.js +58 -0
  38. package/dist/repositories/FileDeploymentRepository.js.map +1 -0
  39. package/dist/repositories/databaseHandler.d.ts +1 -0
  40. package/dist/repositories/databaseHandler.d.ts.map +1 -0
  41. package/dist/repositories/databaseHandler.js +13 -0
  42. package/dist/repositories/databaseHandler.js.map +1 -0
  43. package/dist/repositories/index.d.ts +4 -0
  44. package/dist/repositories/index.d.ts.map +1 -0
  45. package/dist/repositories/index.js +20 -0
  46. package/dist/repositories/index.js.map +1 -0
  47. package/dist/repositories/jsonFileHandler.d.ts +81 -0
  48. package/dist/repositories/jsonFileHandler.d.ts.map +1 -0
  49. package/dist/repositories/jsonFileHandler.js +223 -0
  50. package/dist/repositories/jsonFileHandler.js.map +1 -0
  51. package/dist/repositories/prismaDBHandler.d.ts +1 -0
  52. package/dist/repositories/prismaDBHandler.d.ts.map +1 -0
  53. package/dist/repositories/prismaDBHandler.js +11 -0
  54. package/dist/repositories/prismaDBHandler.js.map +1 -0
  55. package/dist/schemas/DeploymentSchema.d.ts +309 -0
  56. package/dist/schemas/DeploymentSchema.d.ts.map +1 -0
  57. package/dist/schemas/DeploymentSchema.js +56 -0
  58. package/dist/schemas/DeploymentSchema.js.map +1 -0
  59. package/dist/schemas/index.d.ts +2 -0
  60. package/dist/schemas/index.d.ts.map +1 -0
  61. package/dist/schemas/index.js +18 -0
  62. package/dist/schemas/index.js.map +1 -0
  63. package/dist/strategies/BaseDeploymentStrategy.d.ts +41 -0
  64. package/dist/strategies/BaseDeploymentStrategy.d.ts.map +1 -0
  65. package/dist/strategies/BaseDeploymentStrategy.js +545 -0
  66. package/dist/strategies/BaseDeploymentStrategy.js.map +1 -0
  67. package/dist/strategies/DeploymentStrategy.d.ts +19 -0
  68. package/dist/strategies/DeploymentStrategy.d.ts.map +1 -0
  69. package/dist/strategies/DeploymentStrategy.js +3 -0
  70. package/dist/strategies/DeploymentStrategy.js.map +1 -0
  71. package/dist/strategies/LocalDeploymentStrategy.d.ts +4 -0
  72. package/dist/strategies/LocalDeploymentStrategy.d.ts.map +1 -0
  73. package/dist/strategies/LocalDeploymentStrategy.js +8 -0
  74. package/dist/strategies/LocalDeploymentStrategy.js.map +1 -0
  75. package/dist/strategies/OZDefenderDeploymentStrategy.d.ts +62 -0
  76. package/dist/strategies/OZDefenderDeploymentStrategy.d.ts.map +1 -0
  77. package/dist/strategies/OZDefenderDeploymentStrategy.js +757 -0
  78. package/dist/strategies/OZDefenderDeploymentStrategy.js.map +1 -0
  79. package/dist/strategies/RPCDeploymentStrategy.d.ts +139 -0
  80. package/dist/strategies/RPCDeploymentStrategy.d.ts.map +1 -0
  81. package/dist/strategies/RPCDeploymentStrategy.js +710 -0
  82. package/dist/strategies/RPCDeploymentStrategy.js.map +1 -0
  83. package/dist/strategies/index.d.ts +6 -0
  84. package/dist/strategies/index.d.ts.map +1 -0
  85. package/dist/strategies/index.js +12 -0
  86. package/dist/strategies/index.js.map +1 -0
  87. package/dist/types/config.d.ts +26 -0
  88. package/dist/types/config.d.ts.map +1 -0
  89. package/dist/types/config.js +3 -0
  90. package/dist/types/config.js.map +1 -0
  91. package/dist/types/defender.d.ts +22 -0
  92. package/dist/types/defender.d.ts.map +1 -0
  93. package/dist/types/defender.js +3 -0
  94. package/dist/types/defender.js.map +1 -0
  95. package/dist/types/deployments.d.ts +71 -0
  96. package/dist/types/deployments.d.ts.map +1 -0
  97. package/dist/types/deployments.js +20 -0
  98. package/dist/types/deployments.js.map +1 -0
  99. package/dist/types/index.d.ts +5 -0
  100. package/dist/types/index.d.ts.map +1 -0
  101. package/dist/types/index.js +21 -0
  102. package/dist/types/index.js.map +1 -0
  103. package/dist/types/rpc.d.ts +35 -0
  104. package/dist/types/rpc.d.ts.map +1 -0
  105. package/dist/types/rpc.js +3 -0
  106. package/dist/types/rpc.js.map +1 -0
  107. package/dist/utils/common.d.ts +20 -0
  108. package/dist/utils/common.d.ts.map +1 -0
  109. package/dist/utils/common.js +45 -0
  110. package/dist/utils/common.js.map +1 -0
  111. package/dist/utils/configurationResolver.d.ts +30 -0
  112. package/dist/utils/configurationResolver.d.ts.map +1 -0
  113. package/dist/utils/configurationResolver.js +151 -0
  114. package/dist/utils/configurationResolver.js.map +1 -0
  115. package/dist/utils/contractMapping.d.ts +29 -0
  116. package/dist/utils/contractMapping.d.ts.map +1 -0
  117. package/dist/utils/contractMapping.js +224 -0
  118. package/dist/utils/contractMapping.js.map +1 -0
  119. package/dist/utils/defenderClients.d.ts +5 -0
  120. package/dist/utils/defenderClients.d.ts.map +1 -0
  121. package/dist/utils/defenderClients.js +21 -0
  122. package/dist/utils/defenderClients.js.map +1 -0
  123. package/dist/utils/defenderStore.d.ts +14 -0
  124. package/dist/utils/defenderStore.d.ts.map +1 -0
  125. package/dist/utils/defenderStore.js +92 -0
  126. package/dist/utils/defenderStore.js.map +1 -0
  127. package/dist/utils/diamondAbiGenerator.d.ts +113 -0
  128. package/dist/utils/diamondAbiGenerator.d.ts.map +1 -0
  129. package/dist/utils/diamondAbiGenerator.js +415 -0
  130. package/dist/utils/diamondAbiGenerator.js.map +1 -0
  131. package/dist/utils/diffDeployedFacets.d.ts +26 -0
  132. package/dist/utils/diffDeployedFacets.d.ts.map +1 -0
  133. package/dist/utils/diffDeployedFacets.js +106 -0
  134. package/dist/utils/diffDeployedFacets.js.map +1 -0
  135. package/dist/utils/index.d.ts +16 -0
  136. package/dist/utils/index.d.ts.map +1 -0
  137. package/dist/utils/index.js +35 -0
  138. package/dist/utils/index.js.map +1 -0
  139. package/dist/utils/loupe.d.ts +44 -0
  140. package/dist/utils/loupe.d.ts.map +1 -0
  141. package/dist/utils/loupe.js +128 -0
  142. package/dist/utils/loupe.js.map +1 -0
  143. package/dist/utils/rpcStore.d.ts +36 -0
  144. package/dist/utils/rpcStore.d.ts.map +1 -0
  145. package/dist/utils/rpcStore.js +166 -0
  146. package/dist/utils/rpcStore.js.map +1 -0
  147. package/dist/utils/signer.d.ts +36 -0
  148. package/dist/utils/signer.d.ts.map +1 -0
  149. package/dist/utils/signer.js +91 -0
  150. package/dist/utils/signer.js.map +1 -0
  151. package/dist/utils/txlogging.d.ts +13 -0
  152. package/dist/utils/txlogging.d.ts.map +1 -0
  153. package/dist/utils/txlogging.js +87 -0
  154. package/dist/utils/txlogging.js.map +1 -0
  155. package/dist/utils/workspaceSetup.d.ts +32 -0
  156. package/dist/utils/workspaceSetup.d.ts.map +1 -0
  157. package/dist/utils/workspaceSetup.js +311 -0
  158. package/dist/utils/workspaceSetup.js.map +1 -0
  159. package/docs/DIAMOND_ABI_CONFIGURATION_SUMMARY.md +40 -0
  160. package/docs/DIAMOND_ABI_GENERATION.md +220 -0
  161. package/docs/DIAMOND_ABI_GENERATOR_EXAMPLES.md +1204 -0
  162. package/docs/DIAMOND_ABI_GENERATOR_IMPLEMENTATION.md +947 -0
  163. package/docs/DIAMOND_ABI_GENERATOR_QUICK_REFERENCE.md +336 -0
  164. package/docs/README-DEFENDER.md +394 -0
  165. package/docs/README_DIAMOND_ABI_GENERATOR.md +303 -0
  166. package/docs/ROADMAP.md +250 -0
  167. package/docs/assets/image.png +0 -0
  168. package/docs/defender-integration.md +451 -0
  169. package/docs/diamond_module-BaseStrategy_design-v2.uxf +247 -0
  170. package/docs/diamond_module-BaseStrategy_design.uxf +272 -0
  171. package/docs/monitoring-troubleshooting.md +556 -0
  172. package/docs/testing-guide.md +713 -0
  173. package/examples/Diamond_Config_and_Deployment_examples/diamonds/ProxyDiamond/callbacks/ERC20ProxyFacet.ts +31 -0
  174. package/examples/Diamond_Config_and_Deployment_examples/diamonds/ProxyDiamond/proxydiamond.config.json +27 -0
  175. package/examples/Local_Hardhat_Deployer_Script_example/LocalDiamondDeployer.ts +180 -0
  176. package/examples/OZ_Defender_Deployer_Script_example/OZDiamondDeployer.ts +107 -0
  177. package/examples/OZ_Defender_Deployer_Script_example/run-oz-deploy.ts +17 -0
  178. package/examples/Test_examples/ProxyDiamondDeployment.test.ts +202 -0
  179. package/examples/defender-deployment/.env.example +35 -0
  180. package/examples/defender-deployment/README.md +415 -0
  181. package/examples/defender-deployment/contracts/ExampleDiamond.sol +41 -0
  182. package/examples/defender-deployment/contracts/ExampleFacet1.sol +84 -0
  183. package/examples/defender-deployment/contracts/ExampleFacet2.sol +104 -0
  184. package/examples/defender-deployment/contracts/UpgradeFacet.sol +92 -0
  185. package/examples/defender-deployment/deploy-script.ts +170 -0
  186. package/examples/defender-deployment/diamond-config.json +36 -0
  187. package/examples/defender-deployment/upgrade-script.ts +237 -0
  188. package/examples/hardhat-diamonds-config.example.ts +41 -0
  189. package/package.json +228 -0
  190. package/src/core/CallbackManager.ts +70 -0
  191. package/src/core/DeploymentManager.ts +64 -0
  192. package/src/core/Diamond.ts +197 -0
  193. package/src/core/DiamondDeployer.ts +36 -0
  194. package/src/core/index.ts +4 -0
  195. package/src/index.ts +5 -0
  196. package/src/repositories/DBDeploymentRepository.ts +22 -0
  197. package/src/repositories/DeploymentRepository.ts +12 -0
  198. package/src/repositories/FileDeploymentRepository.ts +67 -0
  199. package/src/repositories/databaseHandler.ts +14 -0
  200. package/src/repositories/index.ts +4 -0
  201. package/src/repositories/jsonFileHandler.ts +252 -0
  202. package/src/repositories/prismaDBHandler.ts +10 -0
  203. package/src/schemas/DeploymentSchema.ts +71 -0
  204. package/src/schemas/index.ts +1 -0
  205. package/src/strategies/BaseDeploymentStrategy.ts +649 -0
  206. package/src/strategies/DeploymentStrategy.ts +25 -0
  207. package/src/strategies/LocalDeploymentStrategy.ts +5 -0
  208. package/src/strategies/OZDefenderDeploymentStrategy.ts +849 -0
  209. package/src/strategies/RPCDeploymentStrategy.ts +881 -0
  210. package/src/strategies/index.ts +5 -0
  211. package/src/types/config.ts +34 -0
  212. package/src/types/defender.ts +24 -0
  213. package/src/types/deployments.ts +102 -0
  214. package/src/types/index.ts +4 -0
  215. package/src/types/rpc.ts +37 -0
  216. package/src/utils/common.ts +54 -0
  217. package/src/utils/configurationResolver.ts +141 -0
  218. package/src/utils/contractMapping.ts +220 -0
  219. package/src/utils/defenderClients.ts +22 -0
  220. package/src/utils/defenderStore.ts +62 -0
  221. package/src/utils/diamondAbiGenerator.ts +523 -0
  222. package/src/utils/diffDeployedFacets.ts +131 -0
  223. package/src/utils/index.ts +15 -0
  224. package/src/utils/loupe.ts +159 -0
  225. package/src/utils/rpcStore.ts +152 -0
  226. package/src/utils/signer.ts +93 -0
  227. package/src/utils/txlogging.ts +97 -0
  228. package/src/utils/workspaceSetup.ts +315 -0
  229. package/test/README.md +136 -0
@@ -0,0 +1,757 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.OZDefenderDeploymentStrategy = void 0;
40
+ const defender_sdk_1 = require("@openzeppelin/defender-sdk");
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const crypto_1 = require("crypto");
43
+ const ethers_1 = require("ethers");
44
+ const fs = __importStar(require("fs"));
45
+ const hardhat_1 = __importDefault(require("hardhat"));
46
+ const path_1 = require("path");
47
+ const types_1 = require("../types");
48
+ const contractMapping_1 = require("../utils/contractMapping");
49
+ const defenderStore_1 = require("../utils/defenderStore");
50
+ const BaseDeploymentStrategy_1 = require("./BaseDeploymentStrategy");
51
+ class OZDefenderDeploymentStrategy extends BaseDeploymentStrategy_1.BaseDeploymentStrategy {
52
+ client;
53
+ // private proposalClient: ProposalClient;
54
+ relayerAddress;
55
+ autoApprove;
56
+ via;
57
+ viaType;
58
+ constructor(apiKey, apiSecret, relayerAddress, autoApprove = false, via, viaType, verbose = true, customClient // Optional for testing
59
+ ) {
60
+ super(verbose);
61
+ this.client = customClient || new defender_sdk_1.Defender({ apiKey, apiSecret });
62
+ // this.proposalClient = new ProposalClient({ apiKey, apiSecret });
63
+ this.relayerAddress = relayerAddress;
64
+ this.via = via;
65
+ this.viaType = viaType;
66
+ this.autoApprove = autoApprove;
67
+ }
68
+ async checkAndUpdateDeployStep(stepName, diamond) {
69
+ const config = diamond.getDiamondConfig();
70
+ const network = config.networkName;
71
+ const deploymentId = `${diamond.diamondName}-${network}-${config.chainId}`;
72
+ const store = new defenderStore_1.DefenderDeploymentStore(diamond.diamondName, deploymentId, config.deploymentsPath);
73
+ const step = store.getStep(stepName);
74
+ if (!step || !step.proposalId)
75
+ return;
76
+ try {
77
+ const deployment = await this.client.deploy.getDeployedContract(step.proposalId);
78
+ const status = deployment.status;
79
+ if (status === 'completed') {
80
+ console.log(chalk_1.default.green(`✅ Defender deployment for ${stepName} completed.`));
81
+ store.updateStatus(stepName, 'executed');
82
+ }
83
+ else if (status === 'failed') {
84
+ console.error(chalk_1.default.red(`❌ Defender deployment for ${stepName} failed.`));
85
+ store.updateStatus(stepName, 'failed');
86
+ throw new Error(`Defender deployment ${step.proposalId} failed for step ${stepName}`);
87
+ }
88
+ else {
89
+ console.log(chalk_1.default.yellow(`⏳ Defender deployment for ${stepName} is still ${status}.`));
90
+ // Optionally you can wait/poll here
91
+ }
92
+ }
93
+ catch (err) {
94
+ console.error(chalk_1.default.red(`⚠️ Error while querying Defender deploy status for ${stepName}:`), err);
95
+ }
96
+ }
97
+ /**
98
+ * Polls the Defender API until the deployment is complete or fails.
99
+ * @param stepName The name of the step to poll.
100
+ * @param diamond The diamond instance.
101
+ * @param options Polling options.
102
+ * @returns The deployment response or null if not found.
103
+ */
104
+ async pollUntilComplete(stepName, diamond, options = {}) {
105
+ const { maxAttempts = process.env.NODE_ENV === 'test' ? 1 : 30, // Increase for manual approval workflows
106
+ // Use shorter delays in test environments
107
+ initialDelayMs = process.env.NODE_ENV === 'test' ? 100 : 8000, maxDelayMs = process.env.NODE_ENV === 'test' ? 1000 : 60000, jitter = true } = options;
108
+ const config = diamond.getDiamondConfig();
109
+ const network = config.networkName;
110
+ const deploymentId = `${diamond.diamondName}-${network}-${config.chainId}`;
111
+ const store = new defenderStore_1.DefenderDeploymentStore(diamond.diamondName, deploymentId, config.deploymentsPath);
112
+ const step = store.getStep(stepName);
113
+ if (!step?.proposalId) {
114
+ console.warn(`⚠️ No Defender deployment ID found for step ${stepName}`);
115
+ return null;
116
+ }
117
+ let attempt = 0;
118
+ let delay = initialDelayMs;
119
+ while (attempt < maxAttempts) {
120
+ try {
121
+ console.log(chalk_1.default.blue(`🔍 Polling deployment status for ${stepName} (ID: ${step.proposalId})...`));
122
+ const deployment = await this.client.deploy.getDeployedContract(step.proposalId);
123
+ console.log(chalk_1.default.gray(`📊 Deployment response:`, JSON.stringify(deployment, null, 2)));
124
+ const status = deployment.status;
125
+ if (status === 'completed') {
126
+ console.log(chalk_1.default.green(`✅ Deployment succeeded for ${stepName}.`));
127
+ store.updateStatus(stepName, 'executed');
128
+ // Update diamond data with deployed contract information
129
+ await this.updateDiamondWithDeployment(diamond, stepName, deployment);
130
+ return deployment;
131
+ }
132
+ if (status === 'failed') {
133
+ console.error(chalk_1.default.red(`❌ Deployment failed for ${stepName}.`));
134
+ store.updateStatus(stepName, 'failed');
135
+ const errorMsg = deployment.error || 'Unknown deployment error';
136
+ // Don't catch this error - let it bubble up immediately
137
+ const error = new Error(`Deployment failed for ${stepName}: ${errorMsg}`);
138
+ error.deployment = deployment;
139
+ throw error;
140
+ }
141
+ if (status === 'submitted') {
142
+ const approvalProcessId = deployment.approvalProcessId;
143
+ const safeTxHash = deployment.safeTxHash;
144
+ if (approvalProcessId) {
145
+ console.log(chalk_1.default.yellow(`⏳ Deployment ${stepName} is submitted and waiting for approval.`));
146
+ console.log(chalk_1.default.blue(`🔗 Please approve in Defender dashboard: https://defender.openzeppelin.com/`));
147
+ if (safeTxHash) {
148
+ console.log(chalk_1.default.blue(`📋 Safe Transaction Hash: ${safeTxHash}`));
149
+ }
150
+ }
151
+ else {
152
+ console.log(chalk_1.default.yellow(`⏳ Deployment ${stepName} is submitted and processing...`));
153
+ }
154
+ }
155
+ else {
156
+ console.log(chalk_1.default.yellow(`⏳ Deployment ${stepName} still ${status}. Retrying in ${delay}ms...`));
157
+ }
158
+ }
159
+ catch (err) {
160
+ // Only catch network/API errors, not deployment failures
161
+ if (err instanceof Error && err.message.includes('Deployment failed')) {
162
+ throw err; // Re-throw deployment failures immediately
163
+ }
164
+ console.error(chalk_1.default.red(`⚠️ Error polling Defender for ${stepName}:`), err);
165
+ if (attempt >= maxAttempts - 1) {
166
+ throw err;
167
+ }
168
+ }
169
+ attempt++;
170
+ // Apply jitter
171
+ const jitterValue = jitter ? await (0, crypto_1.randomInt)(Math.floor(delay / 2)) : 0;
172
+ const sleep = delay + jitterValue;
173
+ await new Promise(res => setTimeout(res, sleep));
174
+ // Exponential backoff
175
+ delay = Math.min(delay * 2, maxDelayMs);
176
+ }
177
+ console.warn(chalk_1.default.red(`⚠️ Deployment for ${stepName} did not complete after ${maxAttempts} attempts.`));
178
+ console.log(chalk_1.default.blue(`🔗 Please check the Defender dashboard for pending approvals: https://defender.openzeppelin.com/`));
179
+ console.log(chalk_1.default.yellow(`📋 Deployment ID: ${step.proposalId}`));
180
+ return null;
181
+ }
182
+ /**
183
+ * Updates the diamond data with deployment information from Defender
184
+ */
185
+ async updateDiamondWithDeployment(diamond, stepName, deployment) {
186
+ const deployedDiamondData = diamond.getDeployedDiamondData();
187
+ const contractAddress = deployment.address;
188
+ if (!contractAddress) {
189
+ console.warn(chalk_1.default.yellow(`⚠️ No contract address found in deployment response for ${stepName}`));
190
+ return;
191
+ }
192
+ if (stepName === 'deploy-diamondcutfacet') {
193
+ // Get DiamondCutFacet interface for function selectors
194
+ let diamondCutFacetFunctionSelectors = [];
195
+ try {
196
+ const diamondCutContractName = await (0, contractMapping_1.getContractName)("DiamondCutFacet", diamond);
197
+ const diamondCutFactory = await hardhat_1.default.ethers.getContractFactory(diamondCutContractName, diamond.getSigner());
198
+ diamondCutFacetFunctionSelectors = [];
199
+ diamondCutFactory.interface.forEachFunction((func) => {
200
+ diamondCutFacetFunctionSelectors.push(func.selector);
201
+ });
202
+ }
203
+ catch (error) {
204
+ console.log(chalk_1.default.yellow(`⚠️ Could not get function selectors for DiamondCutFacet (likely in test environment): ${error}`));
205
+ // Use default selectors for DiamondCutFacet in test environments
206
+ diamondCutFacetFunctionSelectors = ['0x1f931c1c']; // diamondCut function
207
+ }
208
+ deployedDiamondData.DeployedFacets = deployedDiamondData.DeployedFacets || {};
209
+ deployedDiamondData.DeployedFacets["DiamondCutFacet"] = {
210
+ address: contractAddress,
211
+ tx_hash: deployment.txHash || 'defender-deployment',
212
+ version: 0,
213
+ funcSelectors: diamondCutFacetFunctionSelectors,
214
+ };
215
+ // Register the DiamondCutFacet function selectors
216
+ const diamondCutFacetSelectorsRegistry = diamondCutFacetFunctionSelectors.reduce((acc, selector) => {
217
+ acc[selector] = {
218
+ facetName: "DiamondCutFacet",
219
+ priority: diamond.getFacetsConfig()?.DiamondCutFacet?.priority || 1000,
220
+ address: contractAddress,
221
+ action: 0, // RegistryFacetCutAction.Deployed
222
+ };
223
+ return acc;
224
+ }, {});
225
+ diamond.registerFunctionSelectors(diamondCutFacetSelectorsRegistry);
226
+ }
227
+ else if (stepName === 'deploy-diamond') {
228
+ deployedDiamondData.DiamondAddress = contractAddress;
229
+ }
230
+ else if (stepName.startsWith('deploy-')) {
231
+ // Extract facet name from step name
232
+ const facetName = stepName.replace('deploy-', '');
233
+ try {
234
+ // Get facet interface for function selectors
235
+ let facetSelectors = [];
236
+ try {
237
+ const facetFactory = await hardhat_1.default.ethers.getContractFactory(facetName, diamond.getSigner());
238
+ facetSelectors = [];
239
+ facetFactory.interface.forEachFunction((func) => {
240
+ facetSelectors.push(func.selector);
241
+ });
242
+ }
243
+ catch (error) {
244
+ console.log(chalk_1.default.yellow(`⚠️ Could not get function selectors for ${facetName} (likely in test environment): ${error}`));
245
+ // Use empty selectors in test environments
246
+ facetSelectors = [];
247
+ }
248
+ const deployConfig = diamond.getDeployConfig();
249
+ const facetConfig = deployConfig.facets[facetName];
250
+ const availableVersions = Object.keys(facetConfig.versions ?? {}).map(Number);
251
+ const targetVersion = Math.max(...availableVersions);
252
+ deployedDiamondData.DeployedFacets = deployedDiamondData.DeployedFacets || {};
253
+ deployedDiamondData.DeployedFacets[facetName] = {
254
+ address: contractAddress,
255
+ tx_hash: deployment.txHash || 'defender-deployment',
256
+ version: targetVersion,
257
+ funcSelectors: facetSelectors,
258
+ };
259
+ // Update new deployed facets for diamond cut preparation
260
+ const initFn = diamond.newDeployment
261
+ ? facetConfig.versions?.[targetVersion]?.deployInit || ""
262
+ : facetConfig.versions?.[targetVersion]?.upgradeInit || "";
263
+ if (initFn && facetName !== deployConfig.protocolInitFacet) {
264
+ diamond.initializerRegistry.set(facetName, initFn);
265
+ }
266
+ const newFacetData = {
267
+ priority: facetConfig.priority || 1000,
268
+ address: contractAddress,
269
+ tx_hash: deployment.txHash || 'defender-deployment',
270
+ version: targetVersion,
271
+ funcSelectors: facetSelectors,
272
+ deployInclude: facetConfig.versions?.[targetVersion]?.deployInclude || [],
273
+ deployExclude: facetConfig.versions?.[targetVersion]?.deployExclude || [],
274
+ initFunction: initFn,
275
+ verified: false,
276
+ };
277
+ diamond.updateNewDeployedFacets(facetName, newFacetData);
278
+ }
279
+ catch (err) {
280
+ console.warn(chalk_1.default.yellow(`⚠️ Could not get interface for facet ${facetName}: ${err}`));
281
+ }
282
+ }
283
+ diamond.updateDeployedDiamondData(deployedDiamondData);
284
+ }
285
+ async preDeployDiamondTasks(diamond) {
286
+ if (this.verbose) {
287
+ console.log(chalk_1.default.yellowBright(`\n🪓 Pre-deploy diamond tasks for ${diamond.diamondName} from ${this.constructor.name}...`));
288
+ }
289
+ await this.checkAndUpdateDeployStep('deploy-diamondcutfacet', diamond);
290
+ await this.checkAndUpdateDeployStep('deploy-diamond', diamond);
291
+ }
292
+ async deployDiamondTasks(diamond) {
293
+ const diamondConfig = diamond.getDiamondConfig();
294
+ const network = diamondConfig.networkName;
295
+ const deploymentId = `${diamond.diamondName}-${network}-${diamondConfig.chainId}`;
296
+ const store = new defenderStore_1.DefenderDeploymentStore(diamond.diamondName, deploymentId, diamondConfig.deploymentsPath);
297
+ const signer = diamond.getSigner();
298
+ const deployerAddress = await signer.getAddress();
299
+ // ---- Deploy DiamondCutFacet ----
300
+ const stepNameCut = 'deploy-diamondcutfacet';
301
+ const cutStep = store.getStep(stepNameCut);
302
+ if (!cutStep || (cutStep.status !== 'executed' && cutStep.status !== 'failed')) {
303
+ const diamondCutContractName = await (0, contractMapping_1.getContractName)('DiamondCutFacet', diamond);
304
+ const diamondCutArtifact = await (0, contractMapping_1.getContractArtifact)('DiamondCutFacet', diamond);
305
+ // Format artifact for Defender SDK - need build-info format, not individual artifact
306
+ const buildInfo = await this.getBuildInfoForContract('DiamondCutFacet', diamond);
307
+ const cutRequest = {
308
+ network,
309
+ contractName: diamondCutContractName,
310
+ contractPath: diamondCutArtifact.sourceName, // Use the actual source name from artifact
311
+ constructorInputs: [],
312
+ verifySourceCode: true, // TODO Verify this should be true or optional
313
+ artifactPayload: JSON.stringify(buildInfo), // Use build-info format for Defender SDK
314
+ salt: ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(32)), // Add salt for CREATE2 deployment as required by Defender
315
+ };
316
+ let cutDeployment;
317
+ try {
318
+ cutDeployment = await this.client.deploy.deployContract(cutRequest);
319
+ console.log(chalk_1.default.blue(`📊 Initial deployment response:`, JSON.stringify(cutDeployment, null, 2)));
320
+ }
321
+ catch (error) {
322
+ console.log(chalk_1.default.red("❌ Error deploying DiamondCutFacet via Defender:"), error);
323
+ throw error;
324
+ }
325
+ store.saveStep({
326
+ stepName: stepNameCut,
327
+ proposalId: cutDeployment.deploymentId,
328
+ status: 'pending',
329
+ description: 'DiamondCutFacet deployed via Defender DeployClient',
330
+ timestamp: Date.now()
331
+ });
332
+ await this.pollUntilComplete(stepNameCut, diamond);
333
+ console.log(chalk_1.default.blue(`📡 Submitted DiamondCutFacet deploy to Defender: ${cutDeployment.deploymentId}`));
334
+ }
335
+ // ---- Deploy Diamond ----
336
+ const stepNameDiamond = 'deploy-diamond';
337
+ const diamondStep = store.getStep(stepNameDiamond);
338
+ if (!diamondStep || (diamondStep.status !== 'executed' && diamondStep.status !== 'failed')) {
339
+ // First, ensure DiamondCutFacet is fully completed and data is updated
340
+ const cutStep = store.getStep(stepNameCut);
341
+ if (cutStep?.status === 'executed') {
342
+ console.log(chalk_1.default.blue(`🔍 Ensuring DiamondCutFacet data is up to date...`));
343
+ // Force update DiamondCutFacet data if needed
344
+ try {
345
+ const cutDeployment = await this.client.deploy.getDeployedContract(cutStep.proposalId);
346
+ if (cutDeployment.status === 'completed' && cutDeployment.address) {
347
+ await this.updateDiamondWithDeployment(diamond, stepNameCut, cutDeployment);
348
+ console.log(chalk_1.default.green(`✅ DiamondCutFacet data updated: ${cutDeployment.address}`));
349
+ }
350
+ }
351
+ catch (error) {
352
+ console.warn(chalk_1.default.yellow(`⚠️ Could not update DiamondCutFacet data: ${error}`));
353
+ }
354
+ }
355
+ // Get the deployed DiamondCutFacet address
356
+ const deployedDiamondData = diamond.getDeployedDiamondData();
357
+ const diamondCutFacetAddress = deployedDiamondData.DeployedFacets?.['DiamondCutFacet']?.address;
358
+ // If still not found, try to get from Defender directly
359
+ if (!diamondCutFacetAddress) {
360
+ console.log(chalk_1.default.yellow(`⚠️ DiamondCutFacet address not found in deployment data, checking Defender...`));
361
+ const cutStep = store.getStep(stepNameCut);
362
+ if (cutStep?.proposalId) {
363
+ try {
364
+ const cutDeployment = await this.client.deploy.getDeployedContract(cutStep.proposalId);
365
+ if (cutDeployment.status === 'completed' && cutDeployment.address) {
366
+ console.log(chalk_1.default.blue(`🔍 Found DiamondCutFacet address from Defender: ${cutDeployment.address}`));
367
+ // Use this address directly for the Diamond constructor
368
+ const directDiamondCutFacetAddress = cutDeployment.address;
369
+ const diamondContractName = await (0, contractMapping_1.getDiamondContractName)(diamond.diamondName, diamond);
370
+ const diamondArtifact = await (0, contractMapping_1.getContractArtifact)(diamond.diamondName, diamond);
371
+ const buildInfo = await this.getBuildInfoForContract(diamond.diamondName, diamond);
372
+ console.log(chalk_1.default.blue(`🏗️ Diamond deployment configuration (direct Defender lookup):`));
373
+ console.log(chalk_1.default.blue(` Contract Name: ${diamondContractName}`));
374
+ console.log(chalk_1.default.blue(` Contract Path: ${diamondArtifact.sourceName}`));
375
+ console.log(chalk_1.default.blue(` Constructor Params:`));
376
+ console.log(chalk_1.default.blue(` Owner: ${deployerAddress}`));
377
+ console.log(chalk_1.default.blue(` DiamondCutFacet: ${directDiamondCutFacetAddress}`));
378
+ const diamondRequest = {
379
+ network,
380
+ contractName: diamondContractName,
381
+ contractPath: diamondArtifact.sourceName,
382
+ constructorInputs: [deployerAddress, directDiamondCutFacetAddress], // Use address from Defender
383
+ verifySourceCode: true,
384
+ artifactPayload: JSON.stringify(buildInfo),
385
+ salt: ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(32)),
386
+ };
387
+ const diamondDeployment = await this.client.deploy.deployContract(diamondRequest);
388
+ console.log(chalk_1.default.blue(`📊 Diamond deployment response:`, JSON.stringify(diamondDeployment, null, 2)));
389
+ store.saveStep({
390
+ stepName: stepNameDiamond,
391
+ proposalId: diamondDeployment.deploymentId,
392
+ status: 'pending',
393
+ description: 'Diamond deployed via Defender DeployClient',
394
+ timestamp: Date.now()
395
+ });
396
+ await this.pollUntilComplete(stepNameDiamond, diamond);
397
+ console.log(chalk_1.default.blue(`📡 Submitted Diamond deploy to Defender: ${diamondDeployment.deploymentId}`));
398
+ return; // Exit early since we handled the deployment
399
+ }
400
+ }
401
+ catch (error) {
402
+ console.error(chalk_1.default.red(`❌ Could not get DiamondCutFacet from Defender: ${error}`));
403
+ }
404
+ }
405
+ throw new Error('DiamondCutFacet must be deployed before Diamond contract. DiamondCutFacet address not found.');
406
+ }
407
+ console.log(chalk_1.default.blue(`🔗 Using DiamondCutFacet address: ${diamondCutFacetAddress}`));
408
+ const diamondContractName = await (0, contractMapping_1.getDiamondContractName)(diamond.diamondName, diamond);
409
+ const diamondArtifact = await (0, contractMapping_1.getContractArtifact)(diamond.diamondName, diamond);
410
+ const buildInfo = await this.getBuildInfoForContract(diamond.diamondName, diamond);
411
+ // Validate the build info structure
412
+ if (!buildInfo.output?.contracts?.[diamondArtifact.sourceName]?.[diamondContractName]) {
413
+ console.warn(chalk_1.default.yellow(`⚠️ Build info validation warning for ${diamondContractName}`));
414
+ console.warn(chalk_1.default.yellow(` Expected path: output.contracts["${diamondArtifact.sourceName}"]["${diamondContractName}"]`));
415
+ console.warn(chalk_1.default.yellow(` Available contracts:`, Object.keys(buildInfo.output?.contracts || {})));
416
+ }
417
+ console.log(chalk_1.default.blue(`🏗️ Diamond deployment configuration:`));
418
+ console.log(chalk_1.default.blue(` Contract Name: ${diamondContractName}`));
419
+ console.log(chalk_1.default.blue(` Contract Path: ${diamondArtifact.sourceName}`));
420
+ console.log(chalk_1.default.blue(` Constructor Params:`));
421
+ console.log(chalk_1.default.blue(` Owner: ${deployerAddress}`));
422
+ console.log(chalk_1.default.blue(` DiamondCutFacet: ${diamondCutFacetAddress}`));
423
+ const diamondRequest = {
424
+ network,
425
+ contractName: diamondContractName,
426
+ contractPath: diamondArtifact.sourceName,
427
+ constructorInputs: [deployerAddress, diamondCutFacetAddress], // Use actual DiamondCutFacet address instead of ZeroAddress
428
+ verifySourceCode: true, // TODO Verify this should be true or optional
429
+ artifactPayload: JSON.stringify(buildInfo), // Use build-info format for Defender SDK
430
+ salt: ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(32)), // Add salt for CREATE2 deployment as required by Defender
431
+ };
432
+ const diamondDeployment = await this.client.deploy.deployContract(diamondRequest);
433
+ console.log(chalk_1.default.blue(`📊 Diamond deployment response:`, JSON.stringify(diamondDeployment, null, 2)));
434
+ store.saveStep({
435
+ stepName: stepNameDiamond,
436
+ proposalId: diamondDeployment.deploymentId,
437
+ status: 'pending',
438
+ description: 'Diamond deployed via Defender DeployClient',
439
+ timestamp: Date.now()
440
+ });
441
+ await this.pollUntilComplete(stepNameDiamond, diamond);
442
+ console.log(chalk_1.default.blue(`📡 Submitted Diamond deploy to Defender: ${diamondDeployment.deploymentId}`));
443
+ }
444
+ }
445
+ async preDeployFacetsTasks(diamond) {
446
+ const facets = Object.keys(diamond.getDeployConfig().facets);
447
+ for (const facet of facets) {
448
+ await this.checkAndUpdateDeployStep(`deploy-${facet}`, diamond);
449
+ }
450
+ }
451
+ /**
452
+ * deployFacetsTasks
453
+ *
454
+ * Deploys the facets of the diamond using OpenZeppelin Defender.
455
+ *
456
+ * @param diamond
457
+ */
458
+ async deployFacetsTasks(diamond) {
459
+ const deployConfig = diamond.getDeployConfig();
460
+ const facetsConfig = deployConfig.facets;
461
+ const diamondConfig = diamond.getDiamondConfig();
462
+ const network = diamondConfig.networkName;
463
+ const deploymentId = `${diamond.diamondName}-${network}-${diamondConfig.chainId}`;
464
+ const store = new defenderStore_1.DefenderDeploymentStore(diamond.diamondName, deploymentId, diamondConfig.deploymentsPath);
465
+ const signer = diamond.getSigner();
466
+ const facetNamesSorted = Object.keys(facetsConfig).sort((a, b) => {
467
+ return (facetsConfig[a].priority ?? 1000) - (facetsConfig[b].priority ?? 1000);
468
+ });
469
+ for (const facetName of facetNamesSorted) {
470
+ const stepKey = `deploy-${facetName}`;
471
+ const step = store.getStep(stepKey);
472
+ if (step?.status === 'executed') {
473
+ console.log(chalk_1.default.gray(`⏩ Skipping already-deployed facet: ${facetName}`));
474
+ continue;
475
+ }
476
+ const facetConfig = facetsConfig[facetName];
477
+ const deployedVersion = diamond.getDeployedDiamondData().DeployedFacets?.[facetName]?.version ?? -1;
478
+ const availableVersions = Object.keys(facetConfig.versions ?? {}).map(Number);
479
+ const targetVersion = Math.max(...availableVersions);
480
+ if (targetVersion <= deployedVersion && deployedVersion !== -1) {
481
+ console.log(chalk_1.default.gray(`⏩ Skipping facet ${facetName}, already at version ${deployedVersion}`));
482
+ continue;
483
+ }
484
+ console.log(chalk_1.default.cyan(`🔧 Deploying facet ${facetName} to version ${targetVersion}...`));
485
+ const facetContractName = await (0, contractMapping_1.getContractName)(facetName, diamond);
486
+ const facetArtifact = await (0, contractMapping_1.getContractArtifact)(facetName, diamond);
487
+ const buildInfo = await this.getBuildInfoForContract(facetName, diamond);
488
+ const deployRequest = {
489
+ network,
490
+ contractName: facetContractName,
491
+ contractPath: facetArtifact.sourceName,
492
+ constructorInputs: [],
493
+ verifySourceCode: true, // TODO Verify this should be true or optional
494
+ artifactPayload: JSON.stringify(buildInfo), // Use build-info format for Defender SDK
495
+ salt: ethers_1.ethers.hexlify(ethers_1.ethers.randomBytes(32)), // Add salt for CREATE2 deployment as required by Defender // Fixed format to match contracts wrapper structure
496
+ };
497
+ const deployResult = await this.client.deploy.deployContract(deployRequest);
498
+ store.saveStep({
499
+ stepName: stepKey,
500
+ proposalId: deployResult.deploymentId,
501
+ status: 'pending',
502
+ description: `Facet ${facetName} deployment submitted`,
503
+ timestamp: Date.now()
504
+ });
505
+ await this.pollUntilComplete(stepKey, diamond);
506
+ console.log(chalk_1.default.blue(`📡 Submitted deployment for facet ${facetName}: ${deployResult.deploymentId}`));
507
+ }
508
+ }
509
+ /**
510
+ * Performs the diamond cut tasks using OpenZeppelin Defender.
511
+ * @param diamond The diamond instance.
512
+ */
513
+ /**
514
+ * Performs the diamond cut tasks using OpenZeppelin Defender with batching support.
515
+ * @param diamond The diamond instance.
516
+ */
517
+ async performDiamondCutTasks(diamond) {
518
+ const deployedDiamondData = diamond.getDeployedDiamondData();
519
+ const diamondAddress = deployedDiamondData.DiamondAddress;
520
+ const deployConfig = diamond.getDeployConfig();
521
+ const diamondConfig = diamond.getDiamondConfig();
522
+ const network = diamondConfig.networkName;
523
+ const [initCalldata, initAddress] = await this.getInitCalldata(diamond);
524
+ const facetCuts = await this.getFacetCuts(diamond);
525
+ await this.validateNoOrphanedSelectors(facetCuts);
526
+ // If no cuts needed, skip
527
+ if (facetCuts.length === 0) {
528
+ if (this.verbose) {
529
+ console.log(chalk_1.default.yellow('⏩ No DiamondCut operations needed - all facets already deployed and up to date'));
530
+ }
531
+ return;
532
+ }
533
+ // Check for batch size limits
534
+ const MAX_BATCH_SIZE = 10; // Conservative limit for gas and transaction size
535
+ const needsBatching = facetCuts.length > MAX_BATCH_SIZE;
536
+ if (needsBatching) {
537
+ console.log(chalk_1.default.yellow(`⚠️ Large DiamondCut detected (${facetCuts.length} cuts). Splitting into batches...`));
538
+ await this.performBatchedDiamondCut(diamond, facetCuts, initCalldata, initAddress);
539
+ return;
540
+ }
541
+ // Single batch execution
542
+ await this.performSingleDiamondCut(diamond, facetCuts, initCalldata, initAddress);
543
+ }
544
+ /**
545
+ * Perform a single DiamondCut operation
546
+ */
547
+ async performSingleDiamondCut(diamond, facetCuts, initCalldata, initAddress) {
548
+ const deployedDiamondData = diamond.getDeployedDiamondData();
549
+ const diamondAddress = deployedDiamondData.DiamondAddress;
550
+ const deployConfig = diamond.getDeployConfig();
551
+ const diamondConfig = diamond.getDiamondConfig();
552
+ const network = diamondConfig.networkName;
553
+ if (this.verbose) {
554
+ console.log(chalk_1.default.yellowBright(`\n🪓 Performing DiamondCut with ${facetCuts.length} cut(s):`));
555
+ for (const cut of facetCuts) {
556
+ console.log(chalk_1.default.bold(`- ${types_1.FacetCutAction[cut.action]} for facet ${cut.name} at ${cut.facetAddress}`));
557
+ console.log(chalk_1.default.gray(` Selectors:`), cut.functionSelectors);
558
+ }
559
+ if (initAddress !== ethers_1.ethers.ZeroAddress) {
560
+ console.log(chalk_1.default.cyan(`Initializing with functionSelector ${initCalldata} on ProtocolInitFacet ${deployConfig.protocolInitFacet} @ ${initAddress}`));
561
+ }
562
+ }
563
+ const proposal = {
564
+ contract: {
565
+ address: diamondAddress,
566
+ network,
567
+ },
568
+ title: `DiamondCut ${facetCuts.length} facets`,
569
+ description: 'Perform diamondCut via Defender',
570
+ type: 'custom',
571
+ functionInterface: {
572
+ name: 'diamondCut',
573
+ inputs: [
574
+ {
575
+ name: 'facetCuts',
576
+ type: 'tuple[]',
577
+ components: [
578
+ { name: 'facetAddress', type: 'address' },
579
+ { name: 'action', type: 'uint8' },
580
+ { name: 'functionSelectors', type: 'bytes4[]' }
581
+ ]
582
+ },
583
+ { name: 'initAddress', type: 'address' },
584
+ { name: 'initCalldata', type: 'bytes' },
585
+ ],
586
+ },
587
+ functionInputs: [
588
+ JSON.stringify(facetCuts.map(cut => ({
589
+ facetAddress: cut.facetAddress,
590
+ action: cut.action,
591
+ functionSelectors: cut.functionSelectors
592
+ }))),
593
+ initAddress,
594
+ initCalldata
595
+ ],
596
+ via: this.via,
597
+ viaType: this.viaType,
598
+ };
599
+ try {
600
+ const { proposalId, url } = await this.client.proposal.create({ proposal });
601
+ console.log(chalk_1.default.blue(`📡 Defender Proposal created: ${url}`));
602
+ // Store the proposal
603
+ const store = new defenderStore_1.DefenderDeploymentStore(diamond.diamondName, `${diamond.diamondName}-${network}-${diamondConfig.chainId}`, diamondConfig.deploymentsPath);
604
+ store.saveStep({
605
+ stepName: 'diamond-cut',
606
+ proposalId,
607
+ status: 'pending',
608
+ description: `DiamondCut proposal with ${facetCuts.length} facets`,
609
+ timestamp: Date.now()
610
+ });
611
+ if (this.autoApprove) {
612
+ await this.handleAutoApproval(proposalId, url);
613
+ }
614
+ else {
615
+ console.log(chalk_1.default.blue(`🔗 Manual approval required: ${url}`));
616
+ }
617
+ }
618
+ catch (error) {
619
+ if (error.response?.status === 402) {
620
+ throw new Error('Defender account billing issue. Please check your Defender account subscription and billing status.');
621
+ }
622
+ else if (error.response?.status === 400) {
623
+ throw new Error(`DiamondCut request invalid. This may be due to gas limits with ${facetCuts.length} cuts. Consider reducing batch size.`);
624
+ }
625
+ throw error;
626
+ }
627
+ }
628
+ /**
629
+ * Perform batched DiamondCut operations
630
+ */
631
+ async performBatchedDiamondCut(diamond, allFacetCuts, initCalldata, initAddress) {
632
+ const MAX_BATCH_SIZE = 10;
633
+ const batches = [];
634
+ // Split into batches
635
+ for (let i = 0; i < allFacetCuts.length; i += MAX_BATCH_SIZE) {
636
+ batches.push(allFacetCuts.slice(i, i + MAX_BATCH_SIZE));
637
+ }
638
+ console.log(chalk_1.default.blue(`📦 Splitting ${allFacetCuts.length} cuts into ${batches.length} batches`));
639
+ // Execute batches sequentially
640
+ for (let batchIndex = 0; batchIndex < batches.length; batchIndex++) {
641
+ const batch = batches[batchIndex];
642
+ const isLastBatch = batchIndex === batches.length - 1;
643
+ // Only use init on the last batch
644
+ const batchInitCalldata = isLastBatch ? initCalldata : '0x';
645
+ const batchInitAddress = isLastBatch ? initAddress : ethers_1.ethers.ZeroAddress;
646
+ console.log(chalk_1.default.blue(`🔄 Processing batch ${batchIndex + 1}/${batches.length} (${batch.length} cuts)`));
647
+ try {
648
+ await this.performSingleDiamondCut(diamond, batch, batchInitCalldata, batchInitAddress);
649
+ console.log(chalk_1.default.green(`✅ Batch ${batchIndex + 1} completed successfully`));
650
+ // Wait between batches to avoid rate limiting
651
+ if (batchIndex < batches.length - 1) {
652
+ console.log(chalk_1.default.gray('⏳ Waiting 5 seconds before next batch...'));
653
+ await new Promise(resolve => setTimeout(resolve, 5000));
654
+ }
655
+ }
656
+ catch (error) {
657
+ console.error(chalk_1.default.red(`❌ Batch ${batchIndex + 1} failed:`), error);
658
+ throw new Error(`DiamondCut batch ${batchIndex + 1} failed: ${error instanceof Error ? error.message : error}`);
659
+ }
660
+ }
661
+ console.log(chalk_1.default.green(`🎉 All ${batches.length} DiamondCut batches completed successfully!`));
662
+ }
663
+ /**
664
+ * Handle auto-approval for proposals
665
+ */
666
+ async handleAutoApproval(proposalId, url) {
667
+ const maxAttempts = 30;
668
+ const delayMs = 8000;
669
+ let attempts = 0;
670
+ console.log(chalk_1.default.blue(`⚡ Auto-approving proposal ${proposalId}...`));
671
+ while (attempts < maxAttempts) {
672
+ try {
673
+ // For auto-approval, we'll just log the status
674
+ // Note: The actual execution method may vary by Defender API version
675
+ console.log(chalk_1.default.gray(`⌛ Proposal status check ${attempts + 1}/${maxAttempts}. Manual execution may be required.`));
676
+ }
677
+ catch (err) {
678
+ console.error(chalk_1.default.red(`⚠️ Error checking proposal status:`), err);
679
+ if (attempts >= maxAttempts - 1) {
680
+ throw err;
681
+ }
682
+ }
683
+ await new Promise((res) => setTimeout(res, delayMs));
684
+ attempts++;
685
+ }
686
+ if (attempts >= maxAttempts) {
687
+ console.warn(chalk_1.default.red(`⚠️ Proposal polling completed after ${maxAttempts} attempts.`));
688
+ console.log(chalk_1.default.blue(`🔗 Manual execution may be required: ${url}`));
689
+ }
690
+ }
691
+ /**
692
+ * Get build-info format for a contract that Defender SDK expects
693
+ */
694
+ async getBuildInfoForContract(contractName, diamond) {
695
+ try {
696
+ // Get the contract artifact first to determine the source path
697
+ const artifact = await (0, contractMapping_1.getContractArtifact)(contractName, diamond);
698
+ const sourceName = artifact.sourceName;
699
+ // Try to find the build-info file that contains this contract
700
+ const buildInfoPath = (0, path_1.join)(process.cwd(), 'artifacts', 'build-info');
701
+ const buildInfoFiles = fs.readdirSync(buildInfoPath);
702
+ for (const fileName of buildInfoFiles) {
703
+ if (!fileName.endsWith('.json'))
704
+ continue;
705
+ const filePath = (0, path_1.join)(buildInfoPath, fileName);
706
+ const buildInfo = JSON.parse(fs.readFileSync(filePath, 'utf8'));
707
+ // Check if this build-info contains our contract
708
+ if (buildInfo.output?.contracts?.[sourceName]?.[contractName]) {
709
+ return buildInfo;
710
+ }
711
+ }
712
+ // Fallback: create a minimal build-info structure
713
+ console.warn(`⚠️ Could not find build-info for ${contractName}, creating minimal structure`);
714
+ return {
715
+ input: {
716
+ language: 'Solidity',
717
+ sources: {
718
+ [sourceName]: {
719
+ content: '// Source not available'
720
+ }
721
+ },
722
+ settings: {
723
+ optimizer: { enabled: true, runs: 1000 },
724
+ outputSelection: {
725
+ '*': {
726
+ '*': ['abi', 'evm.bytecode', 'evm.deployedBytecode']
727
+ }
728
+ }
729
+ }
730
+ },
731
+ output: {
732
+ contracts: {
733
+ [sourceName]: {
734
+ [contractName]: artifact
735
+ }
736
+ }
737
+ }
738
+ };
739
+ }
740
+ catch (error) {
741
+ console.warn(`⚠️ Error getting build info for ${contractName}:`, error);
742
+ // Return a minimal structure as fallback
743
+ const artifact = await (0, contractMapping_1.getContractArtifact)(contractName, diamond);
744
+ return {
745
+ output: {
746
+ contracts: {
747
+ [artifact.sourceName]: {
748
+ [contractName]: artifact
749
+ }
750
+ }
751
+ }
752
+ };
753
+ }
754
+ }
755
+ }
756
+ exports.OZDefenderDeploymentStrategy = OZDefenderDeploymentStrategy;
757
+ //# sourceMappingURL=OZDefenderDeploymentStrategy.js.map