@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,62 @@
1
+ import { join } from 'path';
2
+ import * as fs from 'fs-extra';
3
+ import { DefenderDeploymentRegistry, DefenderProposalStatus, DefenderStepRecord } from '../types/defender';
4
+
5
+ export class DefenderDeploymentStore {
6
+ private readonly filePath: string;
7
+ private readonly diamondName: string;
8
+ private readonly deploymentId: string;
9
+
10
+ constructor(diamondName: string, deploymentId: string, baseDir: string = 'diamonds') {
11
+ this.diamondName = diamondName;
12
+ this.deploymentId = deploymentId;
13
+ const registryDir = join(baseDir, diamondName, 'deployments', 'defender');
14
+ fs.ensureDirSync(registryDir);
15
+ this.filePath = join(registryDir, `${deploymentId}.json`);
16
+ }
17
+
18
+ private loadRegistry(): DefenderDeploymentRegistry {
19
+ if (!fs.existsSync(this.filePath)) {
20
+ return {
21
+ diamondName: this.diamondName,
22
+ deploymentId: this.deploymentId,
23
+ network: '',
24
+ steps: []
25
+ };
26
+ }
27
+ return fs.readJSONSync(this.filePath);
28
+ }
29
+
30
+ private saveRegistry(registry: DefenderDeploymentRegistry) {
31
+ fs.writeJSONSync(this.filePath, registry, { spaces: 2 });
32
+ }
33
+
34
+ public saveStep(step: DefenderStepRecord): void {
35
+ const registry = this.loadRegistry();
36
+ const existing = registry.steps.find(s => s.stepName === step.stepName);
37
+ if (existing) {
38
+ Object.assign(existing, step);
39
+ } else {
40
+ registry.steps.push(step);
41
+ }
42
+ this.saveRegistry(registry);
43
+ }
44
+
45
+ public getStep(stepName: string): DefenderStepRecord | undefined {
46
+ return this.loadRegistry().steps.find(s => s.stepName === stepName);
47
+ }
48
+
49
+ public updateStatus(stepName: string, status: DefenderProposalStatus): void {
50
+ const registry = this.loadRegistry();
51
+ const step = registry.steps.find(s => s.stepName === stepName);
52
+ if (step) {
53
+ step.status = status;
54
+ step.timestamp = Date.now();
55
+ this.saveRegistry(registry);
56
+ }
57
+ }
58
+
59
+ public list(): DefenderStepRecord[] {
60
+ return this.loadRegistry().steps;
61
+ }
62
+ }
@@ -0,0 +1,523 @@
1
+ import chalk from 'chalk';
2
+ import { Interface } from 'ethers';
3
+ import { mkdirSync, writeFileSync } from 'fs';
4
+ import { join } from 'path';
5
+ import { Diamond } from '../core/Diamond';
6
+ import { FacetCuts, RegistryFacetCutAction } from '../types';
7
+ import { getContractArtifact } from '../utils/contractMapping';
8
+
9
+ type AbiItem = {
10
+ type: string;
11
+ name?: string;
12
+ [key: string]: unknown;
13
+ };
14
+
15
+ /**
16
+ * Interface for ABI generation options
17
+ */
18
+ export interface DiamondAbiGenerationOptions {
19
+ /** Diamond instance to generate ABI for */
20
+ diamond: Diamond;
21
+ /** Output directory for generated ABI files */
22
+ outputDir?: string;
23
+ /** Whether to include source information in ABI */
24
+ includeSourceInfo?: boolean;
25
+ /** Whether to validate function selector uniqueness */
26
+ validateSelectors?: boolean;
27
+ /** Whether to log verbose output */
28
+ verbose?: boolean;
29
+ /** Custom facet cuts to include (for preview/planning) */
30
+ customFacetCuts?: FacetCuts;
31
+ }
32
+
33
+ /**
34
+ * Result of ABI generation
35
+ */
36
+ export interface DiamondAbiGenerationResult {
37
+ /** Generated combined ABI */
38
+ abi: AbiItem[];
39
+ /** Function selector to facet mapping */
40
+ selectorMap: Record<string, string>;
41
+ /** Facet addresses included in the ABI */
42
+ facetAddresses: string[];
43
+ /** Output file path */
44
+ outputPath?: string;
45
+ /** Statistics about the generation */
46
+ stats: {
47
+ totalFunctions: number;
48
+ totalEvents: number;
49
+ totalErrors: number;
50
+ facetCount: number;
51
+ duplicateSelectorsSkipped: number;
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Comprehensive Diamond ABI Generator
57
+ *
58
+ * This class generates a complete ABI for a diamond contract based on:
59
+ * - Current deployment state (deployed facets)
60
+ * - Planned diamond cuts (upgrades, additions, removals)
61
+ * - Function selector registry
62
+ * - Deployment configuration
63
+ */
64
+ export class DiamondAbiGenerator {
65
+ private diamond: Diamond;
66
+ private options: DiamondAbiGenerationOptions;
67
+ private seenSelectors: Set<string> = new Set();
68
+ private selectorToFacet: Record<string, string> = {};
69
+ private combinedAbi: AbiItem[] = [];
70
+ private stats = {
71
+ totalFunctions: 0,
72
+ totalEvents: 0,
73
+ totalErrors: 0,
74
+ facetCount: 0,
75
+ duplicateSelectorsSkipped: 0
76
+ };
77
+
78
+ constructor(options: DiamondAbiGenerationOptions) {
79
+ this.diamond = options.diamond;
80
+ this.options = {
81
+ outputDir: options.outputDir || this.diamond.getDiamondAbiPath(),
82
+ includeSourceInfo: true,
83
+ validateSelectors: true,
84
+ verbose: false,
85
+ ...options
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Generate the complete diamond ABI
91
+ */
92
+ async generateAbi(): Promise<DiamondAbiGenerationResult> {
93
+ if (this.options.verbose) {
94
+ console.log(chalk.blue('šŸ”§ Generating Diamond ABI...'));
95
+ }
96
+
97
+ // Reset state
98
+ this.seenSelectors.clear();
99
+ this.selectorToFacet = {};
100
+ this.combinedAbi = [];
101
+ this.stats = {
102
+ totalFunctions: 0,
103
+ totalEvents: 0,
104
+ totalErrors: 0,
105
+ facetCount: 0,
106
+ duplicateSelectorsSkipped: 0
107
+ };
108
+
109
+ // Get facets to include in ABI
110
+ const facetsToInclude = await this.getFacetsToInclude();
111
+
112
+ // Process each facet
113
+ for (const [facetName, facetInfo] of Object.entries(facetsToInclude)) {
114
+ await this.processFacet(facetName, facetInfo);
115
+ }
116
+
117
+ // Add DiamondLoupe functions if not already included
118
+ await this.ensureDiamondLoupeFunctions();
119
+
120
+ // Sort ABI for consistency
121
+ this.sortAbi();
122
+
123
+ // Generate output
124
+ const result = await this.generateOutput();
125
+
126
+ if (this.options.verbose) {
127
+ this.logStats();
128
+ }
129
+
130
+ return result;
131
+ }
132
+
133
+ /**
134
+ * Get all facets that should be included in the ABI
135
+ */
136
+ private async getFacetsToInclude(): Promise<Record<string, FacetInfo>> {
137
+ const facetsToInclude: Record<string, FacetInfo> = {};
138
+
139
+ // 1. Get currently deployed facets
140
+ const deployedData = this.diamond.getDeployedDiamondData();
141
+ if (deployedData.DeployedFacets) {
142
+ for (const [facetName, facetData] of Object.entries(deployedData.DeployedFacets)) {
143
+ if (facetData.address && facetData.funcSelectors) {
144
+ facetsToInclude[facetName] = {
145
+ address: facetData.address,
146
+ selectors: facetData.funcSelectors,
147
+ action: RegistryFacetCutAction.Deployed,
148
+ source: 'deployed'
149
+ };
150
+ }
151
+ }
152
+ }
153
+
154
+ // 2. Get planned cuts from function selector registry
155
+ const registry = this.diamond.functionSelectorRegistry;
156
+ for (const [selector, entry] of registry.entries()) {
157
+ if (entry.action === RegistryFacetCutAction.Add || entry.action === RegistryFacetCutAction.Replace) {
158
+ const facetName = entry.facetName;
159
+ if (!facetsToInclude[facetName]) {
160
+ facetsToInclude[facetName] = {
161
+ address: entry.address,
162
+ selectors: [],
163
+ action: entry.action,
164
+ source: 'registry'
165
+ };
166
+ }
167
+ facetsToInclude[facetName].selectors.push(selector);
168
+ } else if (entry.action === RegistryFacetCutAction.Remove) {
169
+ // Remove selector from existing facet
170
+ const facetName = entry.facetName;
171
+ if (facetsToInclude[facetName]) {
172
+ facetsToInclude[facetName].selectors = facetsToInclude[facetName].selectors.filter(s => s !== selector);
173
+ }
174
+ }
175
+ }
176
+
177
+ // 3. Apply custom facet cuts if provided
178
+ if (this.options.customFacetCuts) {
179
+ for (const cut of this.options.customFacetCuts) {
180
+ const facetName = cut.name;
181
+ if (cut.action === 0) { // Add
182
+ if (!facetsToInclude[facetName]) {
183
+ facetsToInclude[facetName] = {
184
+ address: cut.facetAddress,
185
+ selectors: [],
186
+ action: RegistryFacetCutAction.Add,
187
+ source: 'custom'
188
+ };
189
+ }
190
+ facetsToInclude[facetName].selectors.push(...cut.functionSelectors);
191
+ } else if (cut.action === 2) { // Remove
192
+ if (facetsToInclude[facetName]) {
193
+ facetsToInclude[facetName].selectors = facetsToInclude[facetName].selectors.filter(
194
+ s => !cut.functionSelectors.includes(s)
195
+ );
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ // Remove facets with no selectors
202
+ for (const [facetName, facetInfo] of Object.entries(facetsToInclude)) {
203
+ if (facetInfo.selectors.length === 0) {
204
+ delete facetsToInclude[facetName];
205
+ }
206
+ }
207
+
208
+ return facetsToInclude;
209
+ }
210
+
211
+ /**
212
+ * Process a single facet and add its ABI items
213
+ */
214
+ private async processFacet(facetName: string, facetInfo: FacetInfo): Promise<void> {
215
+ try {
216
+ if (this.options.verbose) {
217
+ console.log(chalk.cyan(`šŸ“„ Processing ${facetName}...`));
218
+ }
219
+
220
+ // Get the contract artifact
221
+ let artifact;
222
+ try {
223
+ artifact = await getContractArtifact(facetName, this.diamond);
224
+ } catch (artifactError) {
225
+ if (this.options.verbose) {
226
+ console.log(chalk.yellow(`āš ļø Artifact loading failed for ${facetName}: ${artifactError}`));
227
+ }
228
+ artifact = null;
229
+ }
230
+
231
+ if (!artifact || !artifact.abi) {
232
+ if (this.options.verbose) {
233
+ console.log(chalk.yellow(`āš ļø No ABI found for ${facetName}`));
234
+ }
235
+
236
+ // Still add selectors to mapping even if artifact can't be loaded
237
+ // This handles cases where selectors are in the registry but no artifact exists
238
+ for (const selector of facetInfo.selectors) {
239
+ if (!this.seenSelectors.has(selector)) {
240
+ this.selectorToFacet[selector] = facetName;
241
+ this.seenSelectors.add(selector);
242
+ }
243
+ }
244
+ return;
245
+ }
246
+
247
+ // Create interface for selector calculation
248
+ const iface = new Interface(artifact.abi);
249
+
250
+ // Process each ABI item
251
+ for (const abiItem of artifact.abi) {
252
+ await this.processAbiItem(abiItem as AbiItem, facetName, facetInfo, iface);
253
+ }
254
+
255
+ // For facets from the registry, add any selectors that weren't found in the ABI
256
+ // This handles cases where selectors are manually registered but don't have corresponding ABI items
257
+ if (facetInfo.source === 'registry') {
258
+ for (const selector of facetInfo.selectors) {
259
+ if (!this.seenSelectors.has(selector)) {
260
+ this.selectorToFacet[selector] = facetName;
261
+ this.seenSelectors.add(selector);
262
+ }
263
+ }
264
+ }
265
+
266
+ this.stats.facetCount++;
267
+ } catch (error) {
268
+ console.error(chalk.red(`āŒ Error processing ${facetName}:`), error);
269
+
270
+ // Still add selectors to mapping even if there was an error
271
+ for (const selector of facetInfo.selectors) {
272
+ if (!this.seenSelectors.has(selector)) {
273
+ this.selectorToFacet[selector] = facetName;
274
+ this.seenSelectors.add(selector);
275
+ }
276
+ }
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Process a single ABI item
282
+ */
283
+ private async processAbiItem(
284
+ abiItem: AbiItem,
285
+ facetName: string,
286
+ facetInfo: FacetInfo,
287
+ iface: Interface
288
+ ): Promise<void> {
289
+ // Skip constructors for diamond
290
+ if (abiItem.type === 'constructor') {
291
+ return;
292
+ }
293
+
294
+ // Handle functions
295
+ if (abiItem.type === 'function') {
296
+ const selector = iface.getFunction(abiItem.name as string)?.selector;
297
+
298
+ if (!selector) {
299
+ if (this.options.verbose) {
300
+ console.log(chalk.yellow(`āš ļø Could not calculate selector for ${abiItem.name}`));
301
+ }
302
+ return;
303
+ }
304
+
305
+ // Check if this function should be included
306
+ if (facetInfo.selectors.length > 0 && !facetInfo.selectors.includes(selector)) {
307
+ return; // Function not in the deployment
308
+ }
309
+
310
+ // Check for duplicates
311
+ if (this.seenSelectors.has(selector)) {
312
+ if (this.options.verbose) {
313
+ console.log(chalk.yellow(`āš ļø Skipping duplicate function: ${abiItem.name} (${selector})`));
314
+ }
315
+ this.stats.duplicateSelectorsSkipped++;
316
+ return;
317
+ }
318
+
319
+ this.seenSelectors.add(selector);
320
+ this.selectorToFacet[selector] = facetName;
321
+
322
+ // Add source information if requested
323
+ if (this.options.includeSourceInfo) {
324
+ abiItem._diamondFacet = facetName;
325
+ abiItem._diamondSelector = selector;
326
+ }
327
+
328
+ this.combinedAbi.push(abiItem);
329
+ this.stats.totalFunctions++;
330
+ }
331
+ // Handle events
332
+ else if (abiItem.type === 'event') {
333
+ // Events are global to the diamond, so include all of them
334
+ this.combinedAbi.push(abiItem);
335
+ this.stats.totalEvents++;
336
+ }
337
+ // Handle errors
338
+ else if (abiItem.type === 'error') {
339
+ // Errors are global to the diamond, so include all of them
340
+ this.combinedAbi.push(abiItem);
341
+ this.stats.totalErrors++;
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Ensure DiamondLoupe functions are included
347
+ */
348
+ private async ensureDiamondLoupeFunctions(): Promise<void> {
349
+ const loupeSelectors = ['0xcdffacc6', '0x52ef6b2c', '0xadfca15e', '0x7a0ed627']; // facets(), facetFunctionSelectors(), facetAddresses(), facetAddress()
350
+
351
+ // Check if any loupe selectors are already included
352
+ for (const selector of loupeSelectors) {
353
+ if (!this.seenSelectors.has(selector)) {
354
+ try {
355
+ await this.processFacet('DiamondLoupeFacet', {
356
+ address: '0x0000000000000000000000000000000000000000',
357
+ selectors: [selector],
358
+ action: RegistryFacetCutAction.Deployed,
359
+ source: 'loupe'
360
+ });
361
+ } catch (error) {
362
+ // DiamondLoupeFacet might not be available, continue
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Sort the ABI for consistency
370
+ */
371
+ private sortAbi(): void {
372
+ this.combinedAbi.sort((a, b) => {
373
+ // Sort by type first (functions, events, errors)
374
+ if (a.type !== b.type) {
375
+ const typeOrder = { 'function': 0, 'event': 1, 'error': 2 };
376
+ return (typeOrder[a.type as keyof typeof typeOrder] || 99) - (typeOrder[b.type as keyof typeof typeOrder] || 99);
377
+ }
378
+
379
+ // Then by name
380
+ return (a.name || '').localeCompare(b.name || '');
381
+ });
382
+ }
383
+
384
+ /**
385
+ * Generate the output files and result
386
+ */
387
+ private async generateOutput(): Promise<DiamondAbiGenerationResult> {
388
+ const result: DiamondAbiGenerationResult = {
389
+ abi: this.combinedAbi,
390
+ selectorMap: this.selectorToFacet,
391
+ facetAddresses: [...new Set(Object.values(this.selectorToFacet))],
392
+ stats: { ...this.stats }
393
+ };
394
+
395
+ // Create output directory if specified
396
+ if (this.options.outputDir) {
397
+ mkdirSync(this.options.outputDir, { recursive: true });
398
+
399
+ // Generate the diamond artifact
400
+ const diamondArtifact = {
401
+ "_format": "hh-sol-artifact-1",
402
+ "contractName": this.diamond.diamondName,
403
+ "sourceName": "diamond-abi/DiamondABI.sol",
404
+ "abi": this.combinedAbi,
405
+ "bytecode": "",
406
+ "deployedBytecode": "",
407
+ "linkReferences": {},
408
+ "deployedLinkReferences": {},
409
+ "_diamondMetadata": {
410
+ "generatedAt": new Date().toISOString(),
411
+ "diamondName": this.diamond.diamondName,
412
+ "networkName": this.diamond.networkName,
413
+ "chainId": this.diamond.chainId,
414
+ "selectorMap": this.selectorToFacet,
415
+ "stats": this.stats
416
+ }
417
+ };
418
+
419
+ // Write the combined artifact
420
+ const outputPath = join(this.options.outputDir, `${this.diamond.getDiamondAbiFileName()}.json`);
421
+ writeFileSync(outputPath, JSON.stringify(diamondArtifact, null, 2));
422
+
423
+ // Write a TypeScript interface file
424
+ const interfacePath = join(this.options.outputDir, `${this.diamond.getDiamondAbiFileName()}.d.ts`);
425
+ this.generateTypeScriptInterface(interfacePath);
426
+
427
+ result.outputPath = outputPath;
428
+
429
+ if (this.options.verbose) {
430
+ console.log(chalk.green(`āœ… Diamond ABI artifact created at: ${outputPath}`));
431
+ }
432
+ }
433
+
434
+ return result;
435
+ }
436
+
437
+ /**
438
+ * Generate TypeScript interface for the diamond
439
+ */
440
+ private generateTypeScriptInterface(outputPath: string): void {
441
+ const interfaceContent = `
442
+ // Auto-generated Diamond ABI interface
443
+ // Generated at: ${new Date().toISOString()}
444
+ // Diamond: ${this.diamond.diamondName}
445
+ // Network: ${this.diamond.networkName}
446
+
447
+ export interface ${this.diamond.diamondName}Interface {
448
+ // Function selectors to facet mapping
449
+ readonly selectorMap: {
450
+ ${Object.entries(this.selectorToFacet).map(([selector, facet]) => ` "${selector}": "${facet}";`).join('\n')}
451
+ };
452
+
453
+ // Facet addresses
454
+ readonly facetAddresses: string[];
455
+
456
+ // ABI for ethers.js Contract instantiation
457
+ readonly abi: AbiItem[];
458
+ }
459
+
460
+ export const ${this.diamond.diamondName}ABI: ${this.diamond.diamondName}Interface;
461
+ `;
462
+
463
+ writeFileSync(outputPath, interfaceContent);
464
+ }
465
+
466
+ /**
467
+ * Log generation statistics
468
+ */
469
+ private logStats(): void {
470
+ console.log(chalk.blue('\nšŸ“Š Diamond ABI Generation Statistics:'));
471
+ console.log(chalk.cyan(` Functions: ${this.stats.totalFunctions}`));
472
+ console.log(chalk.cyan(` Events: ${this.stats.totalEvents}`));
473
+ console.log(chalk.cyan(` Errors: ${this.stats.totalErrors}`));
474
+ console.log(chalk.cyan(` Facets: ${this.stats.facetCount}`));
475
+ console.log(chalk.cyan(` Unique selectors: ${this.seenSelectors.size}`));
476
+ if (this.stats.duplicateSelectorsSkipped > 0) {
477
+ console.log(chalk.yellow(` Duplicate selectors skipped: ${this.stats.duplicateSelectorsSkipped}`));
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Static method to generate ABI for a diamond
483
+ */
484
+ static async generate(options: DiamondAbiGenerationOptions): Promise<DiamondAbiGenerationResult> {
485
+ const generator = new DiamondAbiGenerator(options);
486
+ return generator.generateAbi();
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Interface for facet information used in ABI generation
492
+ */
493
+ interface FacetInfo {
494
+ address: string;
495
+ selectors: string[];
496
+ action: RegistryFacetCutAction;
497
+ source: 'deployed' | 'registry' | 'custom' | 'loupe';
498
+ }
499
+
500
+ /**
501
+ * Generate ABI for a diamond (convenience function)
502
+ */
503
+ export async function generateDiamondAbi(
504
+ diamond: Diamond,
505
+ options: Partial<DiamondAbiGenerationOptions> = {}
506
+ ): Promise<DiamondAbiGenerationResult> {
507
+ return DiamondAbiGenerator.generate({ diamond, ...options });
508
+ }
509
+
510
+ /**
511
+ * Preview what the ABI would look like after planned cuts
512
+ */
513
+ export async function previewDiamondAbi(
514
+ diamond: Diamond,
515
+ plannedCuts: FacetCuts,
516
+ options: Partial<DiamondAbiGenerationOptions> = {}
517
+ ): Promise<DiamondAbiGenerationResult> {
518
+ return DiamondAbiGenerator.generate({
519
+ diamond,
520
+ customFacetCuts: plannedCuts,
521
+ ...options
522
+ });
523
+ }