@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,649 @@
1
+ import "@nomicfoundation/hardhat-ethers";
2
+ import chalk from "chalk";
3
+ import { ethers } from "ethers";
4
+ import hre from "hardhat";
5
+ import { Diamond } from "../core/Diamond";
6
+ import { DeployedFacet } from "../schemas";
7
+ import {
8
+ CallbackArgs,
9
+ FacetCutAction,
10
+ FacetCuts,
11
+ FacetDeploymentInfo,
12
+ FunctionSelectorRegistryEntry,
13
+ NewDeployedFacet,
14
+ RegistryFacetCutAction
15
+ } from "../types";
16
+ import { getContractName, getDeployedFacetInterfaces, getDiamondContractName, logTx } from "../utils";
17
+ import { DeploymentStrategy } from "./DeploymentStrategy";
18
+
19
+ export class BaseDeploymentStrategy implements DeploymentStrategy {
20
+ constructor(protected verbose: boolean = false) { }
21
+
22
+ async preDeployDiamond(diamond: Diamond): Promise<void> {
23
+ if (this.verbose) {
24
+ console.log(chalk.gray(`🔧 Running pre-deploy logic for diamond ${diamond.diamondName}`));
25
+ }
26
+ await this.preDeployDiamondTasks(diamond);
27
+ }
28
+
29
+ protected async preDeployDiamondTasks(diamond: Diamond): Promise<void> { }
30
+
31
+ async deployDiamond(diamond: Diamond): Promise<void> {
32
+ if (this.verbose) {
33
+ console.log(chalk.yellowBright(`\n🪓 Deploying diamond ${diamond.diamondName} with DiamondCutFacet...`));
34
+ }
35
+ await this.deployDiamondTasks(diamond);
36
+ }
37
+
38
+ protected async deployDiamondTasks(diamond: Diamond): Promise<void> {
39
+ console.log(chalk.blueBright(`🚀 Explicitly deploying DiamondCutFacet and Diamond for ${diamond.diamondName}`));
40
+
41
+ // Deploy the DiamondCutFacet - use contract mapping to get correct name
42
+ const diamondCutContractName = await getContractName("DiamondCutFacet", diamond);
43
+ const diamondCutFactory = await hre.ethers.getContractFactory(diamondCutContractName, diamond.getSigner()!);
44
+ const diamondCutFacet = await diamondCutFactory.deploy();
45
+ await diamondCutFacet.waitForDeployment();
46
+
47
+ // Deploy the Diamond - use contract mapping to get correct name
48
+ const diamondContractName = await getDiamondContractName(diamond.diamondName, diamond);
49
+ const diamondFactory = await hre.ethers.getContractFactory(diamondContractName, diamond.getSigner()!);
50
+ const diamondContract = await diamondFactory.deploy(await diamond.getSigner()!.getAddress(), await diamondCutFacet.getAddress());
51
+ await diamondContract.waitForDeployment();
52
+
53
+ // Get function selectors for DiamondCutFacet
54
+ const diamondCutFacetFunctionSelectors: string[] = [];
55
+ diamondCutFacet.interface.forEachFunction((func: ethers.FunctionFragment) => {
56
+ diamondCutFacetFunctionSelectors.push(func.selector);
57
+ });
58
+
59
+ // Get addresses for later use
60
+ const diamondCutFacetAddress = await diamondCutFacet.getAddress();
61
+ const diamondContractAddress = await diamondContract.getAddress();
62
+
63
+ // Register the DiamondCutFacet function selectors
64
+ const diamondCutFacetSelectorsRegistry = diamondCutFacetFunctionSelectors.reduce((acc, selector) => {
65
+ acc[selector] = {
66
+ facetName: "DiamondCutFacet",
67
+ priority: diamond.getFacetsConfig()?.DiamondCutFacet?.priority || 1000, // Default priority if not set
68
+ address: diamondCutFacetAddress,
69
+ action: RegistryFacetCutAction.Deployed,
70
+ };
71
+ return acc;
72
+ }, {} as Record<string, Omit<FunctionSelectorRegistryEntry, "selector">>);
73
+
74
+ diamond.registerFunctionSelectors(diamondCutFacetSelectorsRegistry);
75
+
76
+ // Update deployed diamond data
77
+ const deployedDiamondData = diamond.getDeployedDiamondData();
78
+ deployedDiamondData.DeployerAddress = await diamond.getSigner()!.getAddress();
79
+ deployedDiamondData.DiamondAddress = diamondContractAddress;
80
+ deployedDiamondData.DeployedFacets = deployedDiamondData.DeployedFacets || {};
81
+ deployedDiamondData.DeployedFacets["DiamondCutFacet"] = {
82
+ address: diamondCutFacetAddress,
83
+ tx_hash: diamondCutFacet.deploymentTransaction()?.hash || "",
84
+ version: 0,
85
+ funcSelectors: diamondCutFacetFunctionSelectors,
86
+ };
87
+
88
+ diamond.updateDeployedDiamondData(deployedDiamondData);
89
+
90
+ console.log(chalk.green(`✅ Diamond deployed at ${await diamondContract.getAddress()}, DiamondCutFacet at ${await diamondCutFacet.getAddress()}`));
91
+ }
92
+
93
+ async postDeployDiamond(diamond: Diamond): Promise<void> {
94
+ if (this.verbose) {
95
+ console.log(chalk.gray(`✅ Running post-deploy logic for diamond ${diamond.diamondName}`));
96
+ }
97
+ await this.postDeployDiamondTasks(diamond);
98
+ }
99
+
100
+ protected async postDeployDiamondTasks(diamond: Diamond): Promise<void> { }
101
+
102
+ async preDeployFacets(diamond: Diamond): Promise<void> {
103
+ if (this.verbose) {
104
+ console.log(chalk.gray(`🔧 Running pre-deploy logic for facets of diamond ${diamond.diamondName}`));
105
+ }
106
+ await this.preDeployFacetsTasks(diamond);
107
+ }
108
+
109
+ protected async preDeployFacetsTasks(diamond: Diamond): Promise<void> {
110
+ // This can be overridden by subclasses for custom pre-deploy logic
111
+ if (this.verbose) {
112
+ console.log(chalk.gray(`🔧 No pre-deploy tasks defined for facets of diamond ${diamond.diamondName}`));
113
+ }
114
+ }
115
+
116
+ async deployFacets(diamond: Diamond): Promise<void> {
117
+ await this.deployFacetsTasks(diamond);
118
+ }
119
+
120
+ protected async deployFacetsTasks(diamond: Diamond) {
121
+ const deployConfig = diamond.getDeployConfig();
122
+ const facetsConfig = diamond.getDeployConfig().facets;
123
+ const deployedDiamondData = diamond.getDeployedDiamondData();
124
+ const deployedFacets = deployedDiamondData.DeployedFacets || {};
125
+ const facetCuts: FacetDeploymentInfo[] = [];
126
+
127
+ const sortedFacetNames = Object.keys(deployConfig.facets)
128
+ .sort((a, b) => {
129
+ return (deployConfig.facets[a].priority || 1000) - (deployConfig.facets[b].priority || 1000);
130
+ });
131
+
132
+ // Save the facet deployment info
133
+ for (const facetName of sortedFacetNames) {
134
+ const facetConfig = facetsConfig[facetName];
135
+ const deployedVersion = deployedDiamondData.DeployedFacets?.[facetName]?.version ?? -1;
136
+ const availableVersions = Object.keys(facetConfig.versions || {}).map(Number);
137
+ const upgradeVersion = Math.max(...availableVersions);
138
+
139
+ if (upgradeVersion > deployedVersion || deployedVersion === -1) {
140
+ if (this.verbose) {
141
+ console.log(chalk.blueBright(`🚀 Deploying facet: ${facetName} to version ${upgradeVersion}`));
142
+ }
143
+ // Deploy the facet contract - use contract mapping to get correct name
144
+ const signer = diamond.getSigner()!;
145
+ const facetContractName = await getContractName(facetName, diamond);
146
+ const facetFactory = await hre.ethers.getContractFactory(facetContractName, { signer });
147
+ const facetContract = await facetFactory.deploy();
148
+ await facetContract.waitForDeployment();
149
+
150
+ const deployedFacets = new Map<string, DeployedFacet>();
151
+ const availableVersions = Object.keys(facetConfig.versions ?? {}).map(Number);
152
+ const facetSelectors: string[] = [];
153
+ facetContract.interface.forEachFunction((func: ethers.FunctionFragment) => {
154
+ facetSelectors.push(func.selector);
155
+ });
156
+
157
+ // Initializer function Registry
158
+ const deployInit = facetConfig.versions?.[upgradeVersion]?.deployInit || "";
159
+ const upgradeInit = facetConfig.versions?.[upgradeVersion]?.upgradeInit || "";
160
+
161
+ const initFn = diamond.newDeployment ? deployInit : upgradeInit;
162
+ if (initFn && facetName !== deployConfig.protocolInitFacet) {
163
+ diamond.initializerRegistry.set(facetName, initFn);
164
+ }
165
+
166
+ const newFacetData: NewDeployedFacet = {
167
+ priority: facetConfig.priority || 1000,
168
+ address: await facetContract.getAddress(),
169
+ tx_hash: facetContract.deploymentTransaction()?.hash || "",
170
+ version: upgradeVersion,
171
+ funcSelectors: facetSelectors,
172
+ deployInclude: facetConfig.versions?.[upgradeVersion]?.deployInclude || [],
173
+ deployExclude: facetConfig.versions?.[upgradeVersion]?.deployExclude || [],
174
+ initFunction: initFn,
175
+ verified: false,
176
+ };
177
+
178
+ diamond.updateNewDeployedFacets(facetName, newFacetData);
179
+
180
+ console.log(chalk.cyan(`⛵ ${facetName} deployed at ${await facetContract.getAddress()} with ${facetSelectors.length} selectors.`));
181
+ // Log the deployment transaction and selectors
182
+ if (this.verbose) {
183
+ console.log(chalk.gray(` Selectors:`), facetSelectors);
184
+ }
185
+ }
186
+
187
+ }
188
+ }
189
+
190
+ async postDeployFacets(diamond: Diamond): Promise<void> {
191
+ if (this.verbose) {
192
+ console.log(chalk.gray(`✅ Running post-deploy logic for facets of diamond ${diamond.diamondName}`));
193
+ }
194
+ await this.postDeployFacetsTasks(diamond);
195
+ }
196
+
197
+ // Used by subclasses for facet post-deployment tasks
198
+ protected async postDeployFacetsTasks(diamond: Diamond): Promise<void> { }
199
+
200
+ // Pre-hook for updating function selector registry (can be overridden by subclasses)
201
+ async preUpdateFunctionSelectorRegistry(diamond: Diamond): Promise<void> {
202
+ if (this.verbose) {
203
+ console.log(chalk.gray(`🔧 Running pre-update logic for function selector registry of diamond ${diamond.diamondName}`));
204
+ }
205
+ }
206
+
207
+ protected async preUpdateFunctionSelectorRegistryTasks(diamond: Diamond): Promise<void> { }
208
+
209
+ async updateFunctionSelectorRegistry(diamond: Diamond) {
210
+ if (this.verbose) {
211
+ console.log(chalk.yellowBright(`\n🪓 Updating function selector registry for diamond ${diamond.diamondName}...`));
212
+ }
213
+ this.updateFunctionSelectorRegistryTasks(diamond);
214
+ }
215
+
216
+ protected async updateFunctionSelectorRegistryTasks(
217
+ diamond: Diamond,
218
+ ): Promise<void> {
219
+ const registry = diamond.functionSelectorRegistry;
220
+ const zeroAddress = ethers.ZeroAddress;
221
+
222
+ const newDeployedFacets = diamond.getNewDeployedFacets();
223
+ const newDeployedFacetsByPriority = Object.entries(newDeployedFacets).sort(([, a], [, b]) =>
224
+ (a.priority || 1000) - (b.priority || 1000)
225
+ );
226
+
227
+ for (const [newFacetName, newFacetData] of newDeployedFacetsByPriority) {
228
+ const currentFacetAddress = newFacetData.address;
229
+ const priority: number = newFacetData.priority;
230
+ const functionSelectors: string[] = newFacetData.funcSelectors || [];
231
+ const includeFuncSelectors: string[] = newFacetData.deployInclude || [];
232
+ const excludeFuncSelectors: string[] = newFacetData.deployExclude || [];
233
+
234
+ /* ------------------ Exclusion Filter ------------------ */
235
+ for (const excludeFuncSelector of excludeFuncSelectors) {
236
+ // remove from the facets functionSelectors
237
+ if (excludeFuncSelector in functionSelectors) {
238
+ functionSelectors.splice(functionSelectors.indexOf(excludeFuncSelector), 1);
239
+ }
240
+ // update action to remove if excluded from registry where a previous deployment associated with facetname
241
+ if (excludeFuncSelector in registry && registry.get(excludeFuncSelector)?.facetName === newFacetName) {
242
+ const existing = registry.get(excludeFuncSelector);
243
+ if (existing && existing.facetName === newFacetName) {
244
+ registry.set(excludeFuncSelector, {
245
+ priority: priority,
246
+ address: currentFacetAddress,
247
+ action: RegistryFacetCutAction.Remove,
248
+ facetName: newFacetName,
249
+ });
250
+ }
251
+ }
252
+ }
253
+
254
+ /* ------------ Higher Priority Split of Registry ------------------ */
255
+ const registryHigherPrioritySplit = Array.from(registry.entries())
256
+ .filter(([_, entry]) => entry.priority > priority)
257
+ .reduce((acc, [selector, entry]) => {
258
+ if (!acc[entry.facetName]) {
259
+ acc[entry.facetName] = [];
260
+ }
261
+ acc[entry.facetName].push(selector);
262
+ return acc;
263
+ }, {} as Record<string, string[]>);
264
+
265
+ /* ------------------ Inclusion Override Filter ------------------ */
266
+ for (const includeFuncSelector of includeFuncSelectors) {
267
+ // Force Replace if already registered by higher priority facet
268
+ if (includeFuncSelector in registryHigherPrioritySplit) {
269
+ const higherPriorityFacet = Object.keys(registryHigherPrioritySplit).find(facetName => {
270
+ return registryHigherPrioritySplit[facetName].includes(includeFuncSelector);
271
+ });
272
+ if (higherPriorityFacet) {
273
+ registry.set(includeFuncSelector, {
274
+ priority: priority,
275
+ address: currentFacetAddress,
276
+ action: RegistryFacetCutAction.Replace,
277
+ facetName: newFacetName,
278
+ });
279
+ }
280
+ } else {
281
+ // Add to the registry
282
+ registry.set(includeFuncSelector, {
283
+ priority: priority,
284
+ address: currentFacetAddress,
285
+ action: RegistryFacetCutAction.Add,
286
+ facetName: newFacetName,
287
+ });
288
+ }
289
+
290
+ // remove from the funcSels so it is not modified in Priority Resolution Pass
291
+ if (includeFuncSelector in newDeployedFacets) {
292
+ const existing = newDeployedFacets[newFacetName];
293
+ if (existing && existing.funcSelectors) {
294
+ existing.funcSelectors.splice(existing.funcSelectors.indexOf(includeFuncSelector), 1);
295
+ }
296
+ }
297
+ }
298
+
299
+ /* ------------------ Replace Facet and Priority Resolution Pass ------------- */
300
+ for (const selector of functionSelectors) {
301
+ const existing = registry.get(selector);
302
+ if (existing) {
303
+ const existingPriority = existing.priority;
304
+
305
+ if (existing.facetName === newFacetName) {
306
+ // Same facet, update the address
307
+ registry.set(selector, {
308
+ priority: priority,
309
+ address: currentFacetAddress,
310
+ action: RegistryFacetCutAction.Replace,
311
+ facetName: newFacetName,
312
+ });
313
+ } else if (priority < existingPriority) {
314
+ // Current facet has higher priority, Replace it
315
+ registry.set(selector, {
316
+ priority: priority,
317
+ address: currentFacetAddress,
318
+ action: RegistryFacetCutAction.Replace,
319
+ facetName: newFacetName,
320
+ });
321
+ }
322
+
323
+ } else {
324
+ // New selector, simply add
325
+ registry.set(selector, {
326
+ priority: priority,
327
+ address: currentFacetAddress,
328
+ action: RegistryFacetCutAction.Add,
329
+ facetName: newFacetName,
330
+ });
331
+ }
332
+ }
333
+
334
+ /* ---------------- Remove Old Function Selectors from facets -------------- */
335
+ // Set functionselectors with the newFacetName and still different address to Remove
336
+ for (const [selector, entry] of registry.entries()) {
337
+ if (entry.facetName === newFacetName && entry.address !== currentFacetAddress) {
338
+ registry.set(selector, {
339
+ priority: entry.priority,
340
+ address: zeroAddress,
341
+ action: RegistryFacetCutAction.Remove,
342
+ facetName: newFacetName,
343
+ });
344
+ }
345
+ }
346
+ }
347
+
348
+ // `Remove` function selectors for facets no longer in config (deleted facets)
349
+ const facetsConfig = diamond.getDeployConfig().facets;
350
+ const facetNames = Object.keys(facetsConfig);
351
+ for (const [selector, entry] of registry.entries()) {
352
+ if (!facetNames.includes(entry.facetName)) {
353
+ registry.set(selector, {
354
+ priority: entry.priority,
355
+ address: zeroAddress,
356
+ action: RegistryFacetCutAction.Remove,
357
+ facetName: entry.facetName,
358
+ });
359
+ }
360
+ }
361
+ }
362
+
363
+ async postUpdateFunctionSelectorRegistry(diamond: Diamond): Promise<void> {
364
+ if (this.verbose) {
365
+ console.log(chalk.gray(`✅ Running post-update logic for function selector registry of diamond ${diamond.diamondName}`));
366
+ }
367
+ }
368
+
369
+ async prePerformDiamondCut(diamond: Diamond): Promise<void> {
370
+ if (this.verbose) {
371
+ console.log(chalk.gray(`🔧 Running pre-diamond cut logic for diamond ${diamond.diamondName}`));
372
+ }
373
+ await this.prePerformDiamondCutTasks(diamond);
374
+ }
375
+
376
+ protected async prePerformDiamondCutTasks(diamond: Diamond): Promise<void> {
377
+ // This can be overridden by subclasses for custom pre-diamond cut logic
378
+ if (this.verbose) {
379
+ console.log(chalk.gray(`🔧 No pre-diamond cut tasks defined for diamond ${diamond.diamondName}`));
380
+ }
381
+ }
382
+
383
+ async performDiamondCut(diamond: Diamond): Promise<void> {
384
+ if (this.verbose) {
385
+ console.log(chalk.yellowBright(`\n🪓 Performing diamond cut for diamond ${diamond.diamondName}...`));
386
+ }
387
+ await this.performDiamondCutTasks(diamond);
388
+ }
389
+
390
+ protected async performDiamondCutTasks(diamond: Diamond): Promise<void> {
391
+ const diamondSignerAddress = await diamond.getSigner()?.getAddress()!;
392
+ const signer = await hre.ethers.getSigner(diamondSignerAddress!);
393
+ const diamondContract = await hre.ethers.getContractAt("IDiamondCut", diamond.getDeployedDiamondData().DiamondAddress!);
394
+ const signerDiamondContract = diamondContract.connect(signer);
395
+ const deployConfig = diamond.getDeployConfig();
396
+ const deployedDiamondData = diamond.getDeployedDiamondData();
397
+
398
+ // Setup initCallData with Atomic Protocol Initializer
399
+ const [initCalldata, initAddress] = await this.getInitCalldata(diamond);
400
+
401
+ // extract facet cuts from the selector registry
402
+ const facetCuts: FacetCuts = await this.getFacetCuts(diamond);
403
+
404
+ // Validate no orphaned selectors, i.e. 'Add', 'Replace' or 'Deployed' selectors with the same facetNames but different addresses
405
+ await this.validateNoOrphanedSelectors(facetCuts);
406
+
407
+ if (this.verbose) {
408
+ console.log(chalk.yellowBright(`\n🪓 Performing DiamondCut with ${facetCuts.length} cut(s):`));
409
+ for (const cut of facetCuts) {
410
+ console.log(chalk.bold(`- ${FacetCutAction[cut.action]} for facet ${cut.name} at ${cut.facetAddress}`));
411
+ console.log(chalk.gray(` Selectors:`), cut.functionSelectors);
412
+ }
413
+ if (initAddress !== ethers.ZeroAddress) {
414
+ console.log(chalk.cyan(`Initializing with functionSelector ${initCalldata} on ProtocolInitFacet ${deployConfig.protocolInitFacet} @ ${initAddress}`));
415
+ }
416
+ }
417
+
418
+ /* -------------------------- Perform the diamond cut -----------------------*/
419
+ const facetSelectorCutMap = facetCuts.map(fc => ({ facetAddress: fc.facetAddress, action: fc.action, functionSelectors: fc.functionSelectors }));
420
+ const tx = await (signerDiamondContract as any).diamondCut(
421
+ facetSelectorCutMap,
422
+ initAddress,
423
+ initCalldata
424
+ );
425
+
426
+ /* --------------------- Update the deployed diamond data ------------------ */
427
+ const txHash = tx.hash;
428
+ await this.postDiamondCutDeployedDataUpdate(diamond, txHash);
429
+
430
+ const ifaceList = getDeployedFacetInterfaces(deployedDiamondData);
431
+ // Log the transaction
432
+ if (this.verbose) {
433
+ await logTx(tx, "DiamondCut", ifaceList);
434
+ } else {
435
+ console.log(chalk.blueBright(`🔄 Waiting for DiamondCut transaction to be mined...`));
436
+ await tx.wait();
437
+ }
438
+
439
+
440
+ console.log(chalk.green(`✅ DiamondCut executed: ${tx.hash}`));
441
+
442
+ for (const [facetName, initFunction] of diamond.initializerRegistry.entries()) {
443
+ console.log(chalk.blueBright(`▶ Running ${initFunction} from the ${facetName} facet`));
444
+ // const contract = await ethers.getContractAt(facetName, diamondSignerAddress!);
445
+ const facetContractName = await getContractName(facetName, diamond);
446
+ const initContract = await hre.ethers.getContractAt(facetContractName, diamond.getDeployedDiamondData().DiamondAddress!);
447
+ const signerDiamondContract = initContract.connect(signer);
448
+
449
+ const tx = await initContract[initFunction]();
450
+ // const tx = await signerDiamondContract.initFunction;
451
+ if (this.verbose) {
452
+ logTx(tx, `${facetName}.${initFunction}`, ifaceList);
453
+ } else {
454
+ console.log(chalk.blueBright(`🔄 Waiting for ${facetName}.${initFunction}} mined...`));
455
+ await tx.wait();
456
+ }
457
+ console.log(chalk.green(`✅ ${facetName}.${initFunction} executed`));
458
+ }
459
+ }
460
+
461
+ async postPerformDiamondCut(diamond: Diamond): Promise<void> {
462
+ if (this.verbose) {
463
+ console.log(chalk.gray(`✅ Running post-diamond cut logic for diamond ${diamond.diamondName}`));
464
+ }
465
+ await this.postPerformDiamondCutTasks(diamond);
466
+ }
467
+
468
+ protected async postPerformDiamondCutTasks(diamond: Diamond): Promise<void> { }
469
+
470
+ async getInitCalldata(diamond: Diamond): Promise<[string, string]> {
471
+ const deployedDiamondData = diamond.getDeployedDiamondData();
472
+ const deployConfig = diamond.getDeployConfig();
473
+
474
+ let initAddress = ethers.ZeroAddress;
475
+ let initCalldata = "0x";
476
+
477
+ const protocolInitFacet = deployConfig.protocolInitFacet || "";
478
+ const protocolVersion = deployConfig.protocolVersion;
479
+ const protocolFacetInfo = diamond.getNewDeployedFacets()[protocolInitFacet];
480
+
481
+ if (protocolInitFacet && protocolFacetInfo) {
482
+ const versionCfg = deployConfig.facets[protocolInitFacet]?.versions?.[protocolVersion];
483
+ const initFn = diamond.newDeployment ? versionCfg?.deployInit : versionCfg?.upgradeInit;
484
+
485
+ if (initFn) {
486
+ const iface = new ethers.Interface([`function ${initFn}`]);
487
+ initAddress = protocolFacetInfo.address!;
488
+ initCalldata = iface.encodeFunctionData(initFn);
489
+ if (this.verbose) {
490
+ console.log(
491
+ chalk.cyan(`🔧 Using protocol-wide initializer: ${protocolInitFacet}.${initFn}()`)
492
+ );
493
+ }
494
+ }
495
+ }
496
+
497
+ if (initAddress === ethers.ZeroAddress) {
498
+ console.log(chalk.yellow(`⚠️ No protocol-wide initializer found. Using zero address.`));
499
+ }
500
+ diamond.setInitAddress(initAddress);
501
+ return [initCalldata, initAddress];
502
+ }
503
+
504
+ async getFacetCuts(diamond: Diamond): Promise<FacetCuts> {
505
+ const deployConfig = diamond.getDeployConfig();
506
+ const selectorRegistry = diamond.functionSelectorRegistry;
507
+ /* -------------------------- Prepare the facet cuts -----------------------*/
508
+ // extract facet cuts from the selector registry
509
+ const facetCuts = Array.from(selectorRegistry.entries())
510
+ .filter(([_, entry]) => entry.action !== RegistryFacetCutAction.Deployed)
511
+ .map(([selector, entry]) => {
512
+ return {
513
+ facetAddress: entry.address,
514
+ action: entry.action,
515
+ functionSelectors: [selector],
516
+ name: entry.facetName,
517
+ };
518
+ });
519
+
520
+ return facetCuts;
521
+ }
522
+
523
+ async validateNoOrphanedSelectors(facetCuts: FacetCuts): Promise<void> {
524
+ // Validate no orphaned selectors, i.e. 'Add', 'Replace' or 'Deployed' selectors with the same facetNames but different addresses
525
+ const orphanedSelectors = facetCuts.filter(facetCut => {
526
+ return facetCuts.some(otherFacetCut => {
527
+ return (
528
+ otherFacetCut.facetAddress !== facetCut.facetAddress &&
529
+ otherFacetCut.name === facetCut.name &&
530
+ (otherFacetCut.action === RegistryFacetCutAction.Add ||
531
+ otherFacetCut.action === RegistryFacetCutAction.Replace ||
532
+ otherFacetCut.action === RegistryFacetCutAction.Deployed)
533
+ );
534
+ });
535
+ });
536
+
537
+ if (orphanedSelectors.length > 0) {
538
+ console.error(chalk.redBright(`❌ Orphaned selectors found for facet ${orphanedSelectors[0].name} at address ${orphanedSelectors[0].facetAddress}`));
539
+ console.error(chalk.redBright(` - ${orphanedSelectors[0].functionSelectors}`));
540
+ throw new Error(`Orphaned selectors found for facet ${orphanedSelectors[0].name} at address ${orphanedSelectors[0].facetAddress}`);
541
+ }
542
+ }
543
+
544
+ async postDiamondCutDeployedDataUpdate(diamond: Diamond, txHash: string): Promise<void> {
545
+ const deployConfig = diamond.getDeployConfig();
546
+ const deployedDiamondData = diamond.getDeployedDiamondData();
547
+ const selectorRegistry = diamond.functionSelectorRegistry;
548
+ const newDeployedFacets = diamond.getNewDeployedFacets();
549
+
550
+ deployedDiamondData.protocolVersion = deployConfig.protocolVersion;
551
+
552
+ // Aggregate selectors for each facet
553
+ const facetSelectorsMap: Record<string, { address: string, selectors: string[] }> = {};
554
+
555
+ for (const [selector, entry] of selectorRegistry.entries()) {
556
+ const facetName = entry.facetName;
557
+ if (!facetSelectorsMap[facetName]) {
558
+ facetSelectorsMap[facetName] = { address: entry.address, selectors: [] };
559
+ }
560
+ if (entry.action === RegistryFacetCutAction.Add || entry.action === RegistryFacetCutAction.Replace || entry.action === RegistryFacetCutAction.Deployed) {
561
+ facetSelectorsMap[facetName].selectors.push(selector);
562
+ }
563
+ }
564
+
565
+ // Update deployed facets with aggregated selectors
566
+ deployedDiamondData.DeployedFacets = deployedDiamondData.DeployedFacets || {};
567
+ for (const [facetName, { address, selectors }] of Object.entries(facetSelectorsMap)) {
568
+ if (selectors.length > 0) {
569
+ let facetVersion: number;
570
+
571
+ if (newDeployedFacets[facetName]) {
572
+ // Use the version from newly deployed facet
573
+ facetVersion = newDeployedFacets[facetName].version;
574
+ } else {
575
+ // For facets not in newDeployedFacets (like existing deployed facets),
576
+ // preserve their existing version or use 0 as default
577
+ facetVersion = deployedDiamondData.DeployedFacets[facetName]?.version ?? 0;
578
+ }
579
+
580
+ deployedDiamondData.DeployedFacets[facetName] = {
581
+ address,
582
+ tx_hash: txHash,
583
+ version: facetVersion,
584
+ funcSelectors: selectors,
585
+ };
586
+ }
587
+ }
588
+
589
+ // Remove facets with no selectors
590
+ for (const facetName of Object.keys(deployedDiamondData.DeployedFacets)) {
591
+ if (!facetSelectorsMap[facetName] || facetSelectorsMap[facetName].selectors.length === 0) {
592
+ delete deployedDiamondData.DeployedFacets[facetName];
593
+ }
594
+ }
595
+
596
+ diamond.updateDeployedDiamondData(deployedDiamondData);
597
+ }
598
+
599
+ async preRunPostDeployCallbacks(diamond: Diamond): Promise<void> {
600
+ if (this.verbose) {
601
+ console.log(chalk.gray(`🔧 Running pre-post-deploy logic for diamond ${diamond.diamondName}`));
602
+ }
603
+ }
604
+
605
+ async runPostDeployCallbacks(diamond: Diamond): Promise<void> {
606
+ await this.runPostDeployCallbacksTasks(diamond);
607
+ }
608
+
609
+ protected async runPostDeployCallbacksTasks(diamond: Diamond): Promise<void> {
610
+ console.log(`🔄 Running post-deployment callbacks...`);
611
+
612
+ const deployConfig = diamond.getDeployConfig();
613
+
614
+ for (const [facetName, facetConfig] of Object.entries(deployConfig.facets)) {
615
+ if (!facetConfig.versions) continue;
616
+
617
+ for (const [version, config] of Object.entries(facetConfig.versions)) {
618
+ if (config.callbacks) {
619
+ const args: CallbackArgs = {
620
+ diamond: diamond,
621
+ };
622
+
623
+ console.log(chalk.cyanBright(`Executing callback ${config.callbacks} for facet ${facetName}...`));
624
+ await diamond.callbackManager.executeCallback(
625
+ facetName,
626
+ config.callbacks,
627
+ args
628
+ );
629
+
630
+ console.log(chalk.magenta(`✅ Callback ${config.callbacks} executed for facet ${facetName}`));
631
+ }
632
+ }
633
+ }
634
+
635
+ console.log(chalk.greenBright`✅ All post-deployment callbacks executed.`);
636
+ }
637
+
638
+ async postRunPostDeployCallbacks(diamond: Diamond): Promise<void> {
639
+ if (this.verbose) {
640
+ console.log(chalk.gray(`✅ Running post-post-deploy logic for diamond ${diamond.diamondName}`));
641
+ }
642
+ }
643
+
644
+ protected async postRunPostDeployCallbacksTasks(diamond: Diamond): Promise<void> {
645
+ if (this.verbose) {
646
+ console.log(chalk.gray(`✅ Running post-post-deploy logic for diamond ${diamond.diamondName}`));
647
+ }
648
+ }
649
+ }
@@ -0,0 +1,25 @@
1
+ import { FacetDeploymentInfo } from "../types";
2
+ import { Diamond } from "../core/Diamond";
3
+ import { DeployedFacet, FacetsConfig } from "../schemas";
4
+
5
+ export interface DeploymentStrategy {
6
+ preDeployDiamond(diamond: Diamond): Promise<void>;
7
+ deployDiamond(diamond: Diamond): Promise<void>;
8
+ postDeployDiamond(diamond: Diamond): Promise<void>;
9
+
10
+ preDeployFacets(diamond: Diamond): Promise<void>;
11
+ deployFacets(diamond: Diamond): Promise<void>;
12
+ postDeployFacets(diamond: Diamond): Promise<void>;
13
+
14
+ preUpdateFunctionSelectorRegistry(diamond: Diamond): Promise<void>;
15
+ updateFunctionSelectorRegistry(diamond: Diamond): Promise<void>;
16
+ postUpdateFunctionSelectorRegistry(diamond: Diamond): Promise<void>;
17
+
18
+ prePerformDiamondCut(diamond: Diamond): Promise<void>;
19
+ performDiamondCut(diamond: Diamond): Promise<void>;
20
+ postPerformDiamondCut(diamond: Diamond): Promise<void>;
21
+
22
+ preRunPostDeployCallbacks(diamond: Diamond): Promise<void>;
23
+ runPostDeployCallbacks(diamond: Diamond): Promise<void>;
24
+ postRunPostDeployCallbacks(diamond: Diamond): Promise<void>;
25
+ }
@@ -0,0 +1,5 @@
1
+ import { BaseDeploymentStrategy } from "./BaseDeploymentStrategy";
2
+
3
+ export class LocalDeploymentStrategy extends BaseDeploymentStrategy {
4
+
5
+ }