@diamondslab/diamonds 1.3.0 → 1.4.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.
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeFacetSelectors = computeFacetSelectors;
4
+ exports.resolveFunctionSelectorRegistry = resolveFunctionSelectorRegistry;
5
+ const ethers_1 = require("ethers");
6
+ const types_1 = require("../types");
7
+ /**
8
+ * Compute a facet's candidate selectors: its raw ABI selectors **minus `deployExclude`**.
9
+ *
10
+ * `deployInclude` is **additive** (INV-3, M2-E1) — it does NOT reduce the candidate set; a facet keeps
11
+ * its other selectors. The override (force-ownership over a higher-priority facet) and the additive
12
+ * priority resolution both happen in `resolveFunctionSelectorRegistry`. Pure.
13
+ *
14
+ * @param facetSelectors the facet's raw ABI function selectors (4-byte, `0x…`)
15
+ * @param _deployInclude the facet's `deployInclude` signatures — accepted for API symmetry; additive
16
+ * (not used to filter the candidate set)
17
+ * @param deployExclude function signatures to remove
18
+ * @returns the candidate selector list (a new array; the input is not mutated)
19
+ */
20
+ function computeFacetSelectors(facetSelectors, _deployInclude, deployExclude) {
21
+ const result = [...facetSelectors];
22
+ // Apply deployExclude (removal) only. deployInclude is additive — it does not filter here.
23
+ for (const excludeSelector of deployExclude) {
24
+ const selectorToExclude = ethers_1.ethers.id(excludeSelector).slice(0, 10);
25
+ const index = result.indexOf(selectorToExclude);
26
+ if (index !== -1) {
27
+ result.splice(index, 1);
28
+ }
29
+ }
30
+ return result;
31
+ }
32
+ /**
33
+ * Resolve ownership of every function selector across the newly-deployed facets and write
34
+ * the result (`Add`/`Replace`/`Remove`/`Deployed` actions) into `registry` **in place**.
35
+ * Pure — no Hardhat/provider. Extracted verbatim from
36
+ * `BaseDeploymentStrategy.updateFunctionSelectorRegistryTasks`.
37
+ */
38
+ function resolveFunctionSelectorRegistry(args) {
39
+ const { registry, newDeployedFacets, facetNames } = args;
40
+ const zeroAddress = ethers_1.ethers.ZeroAddress;
41
+ const newDeployedFacetsByPriority = Object.entries(newDeployedFacets).sort(([, a], [, b]) => (a.priority || 1000) - (b.priority || 1000));
42
+ for (const [newFacetName, newFacetData] of newDeployedFacetsByPriority) {
43
+ const currentFacetAddress = newFacetData.address;
44
+ const priority = newFacetData.priority;
45
+ const includeFuncSelectors = newFacetData.deployInclude || [];
46
+ const excludeFuncSelectors = newFacetData.deployExclude || [];
47
+ // Convert function signatures to selectors
48
+ const includeFuncSelectorsAsSelectors = includeFuncSelectors.map((sig) => ethers_1.ethers.id(sig).slice(0, 10));
49
+ const excludeFuncSelectorsAsSelectors = excludeFuncSelectors.map((sig) => ethers_1.ethers.id(sig).slice(0, 10));
50
+ // Initialize funcSelectors if not present
51
+ if (!newFacetData.funcSelectors) {
52
+ newFacetData.funcSelectors = [];
53
+ }
54
+ const functionSelectors = newFacetData.funcSelectors;
55
+ /* ------------------ Exclusion Filter ------------------ */
56
+ for (const excludeFuncSelector of excludeFuncSelectorsAsSelectors) {
57
+ // remove from the facets functionSelectors
58
+ if (functionSelectors.includes(excludeFuncSelector)) {
59
+ functionSelectors.splice(functionSelectors.indexOf(excludeFuncSelector), 1);
60
+ }
61
+ // update action to remove if excluded from registry where a previous deployment associated with facetname
62
+ if (registry.has(excludeFuncSelector) &&
63
+ registry.get(excludeFuncSelector)?.facetName === newFacetName) {
64
+ const existing = registry.get(excludeFuncSelector);
65
+ if (existing?.facetName === newFacetName) {
66
+ registry.set(excludeFuncSelector, {
67
+ priority: priority,
68
+ // EIP-2535: a Remove cut MUST use address(0) (M3-E2 fix for M3-E1 S-3 —
69
+ // was currentFacetAddress, which reverts on-chain on deployExclude-on-upgrade).
70
+ address: zeroAddress,
71
+ action: types_1.RegistryFacetCutAction.Remove,
72
+ facetName: newFacetName,
73
+ });
74
+ }
75
+ }
76
+ }
77
+ /* ------------------ Inclusion Override Filter ------------------ */
78
+ for (const includeFuncSelector of includeFuncSelectorsAsSelectors) {
79
+ const existingEntry = registry.get(includeFuncSelector);
80
+ if (existingEntry?.address &&
81
+ existingEntry.address !== currentFacetAddress &&
82
+ existingEntry.action !== types_1.RegistryFacetCutAction.Add) {
83
+ // Selector already registered/deployed at a different facet address — a selector moved
84
+ // facets, or a redeployed facet at a new address (the upgrade case; verified M3-E1 S-1).
85
+ // Reconcile -> Replace, not Add (which would revert "Can't add function that already
86
+ // exists"). Subsumes the removed dead higherPrioritySplit branch (M3-E3).
87
+ registry.set(includeFuncSelector, {
88
+ priority: priority,
89
+ address: currentFacetAddress,
90
+ action: types_1.RegistryFacetCutAction.Replace,
91
+ facetName: newFacetName,
92
+ });
93
+ }
94
+ else {
95
+ // Add to the registry
96
+ registry.set(includeFuncSelector, {
97
+ priority: priority,
98
+ address: currentFacetAddress,
99
+ action: types_1.RegistryFacetCutAction.Add,
100
+ facetName: newFacetName,
101
+ });
102
+ }
103
+ // remove from the funcSels so it is not modified in Priority Resolution Pass
104
+ const existing = newDeployedFacets[newFacetName];
105
+ if (existing &&
106
+ existing.funcSelectors &&
107
+ existing.funcSelectors.includes(includeFuncSelector)) {
108
+ existing.funcSelectors.splice(existing.funcSelectors.indexOf(includeFuncSelector), 1);
109
+ }
110
+ }
111
+ /* ------------------ Replace Facet and Priority Resolution Pass ------------- */
112
+ for (const selector of functionSelectors) {
113
+ const existing = registry.get(selector);
114
+ if (existing) {
115
+ const existingPriority = existing.priority;
116
+ if (existing.facetName === newFacetName) {
117
+ // Same facet, update the address
118
+ registry.set(selector, {
119
+ priority: priority,
120
+ address: currentFacetAddress,
121
+ action: types_1.RegistryFacetCutAction.Replace,
122
+ facetName: newFacetName,
123
+ });
124
+ }
125
+ else if (priority < existingPriority) {
126
+ // Current facet has higher priority, Replace it
127
+ registry.set(selector, {
128
+ priority: priority,
129
+ address: currentFacetAddress,
130
+ action: types_1.RegistryFacetCutAction.Replace,
131
+ facetName: newFacetName,
132
+ });
133
+ }
134
+ }
135
+ else {
136
+ // New selector, simply add
137
+ registry.set(selector, {
138
+ priority: priority,
139
+ address: currentFacetAddress,
140
+ action: types_1.RegistryFacetCutAction.Add,
141
+ facetName: newFacetName,
142
+ });
143
+ }
144
+ }
145
+ /* ---------------- Remove Old Function Selectors from facets -------------- */
146
+ // Set functionselectors with the newFacetName and still different address to Remove
147
+ for (const [selector, entry] of registry.entries()) {
148
+ if (entry.facetName === newFacetName && entry.address !== currentFacetAddress) {
149
+ registry.set(selector, {
150
+ priority: entry.priority,
151
+ address: zeroAddress,
152
+ action: types_1.RegistryFacetCutAction.Remove,
153
+ facetName: newFacetName,
154
+ });
155
+ }
156
+ }
157
+ }
158
+ // `Remove` function selectors for facets no longer in config (deleted facets)
159
+ for (const [selector, entry] of registry.entries()) {
160
+ if (!facetNames.includes(entry.facetName)) {
161
+ registry.set(selector, {
162
+ priority: entry.priority,
163
+ address: zeroAddress,
164
+ action: types_1.RegistryFacetCutAction.Remove,
165
+ facetName: entry.facetName,
166
+ });
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=selectorResolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectorResolution.js","sourceRoot":"","sources":["../../src/resolution/selectorResolution.ts"],"names":[],"mappings":"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","sourcesContent":["import { ethers } from 'ethers';\nimport {\n\tFunctionSelectorRegistryEntry,\n\tNewDeployedFacets,\n\tRegistryFacetCutAction,\n} from '../types';\n\n/**\n * Pure selector-resolution core for ERC-2535 diamond deployments.\n *\n * Extracted from `BaseDeploymentStrategy` (M1-E1) so the resolution semantics can be\n * unit-tested without a chain and later promoted into the shared core used by both the\n * deployment strategy and the planned pre-launch config validator.\n *\n * Lifted from the strategy in M1-E1 (behavior-preserving). Status of the original quirks:\n * - the deploy-time `deployInclude` whitelist — **REMOVED in M2-E1**: `deployInclude` is now\n * **additive** (INV-3); a facet keeps its other selectors (the override is resolved below);\n * - the inverted/dead `registryHigherPrioritySplit` (`entry.priority > priority`) — **REMOVED in\n * M3-E3**: it never matched a real conflict; the cases it nominally handled fall through to the\n * `5b2f7af` Replace branch with the identical result;\n * - the `5b2f7af` `Replace`-instead-of-`Add` branch — **verified correct (M3-E1 S-1)**; kept as-is.\n *\n * The module takes NO Hardhat/provider dependency (only `ethers` for selector math).\n */\n\n/** Map<functionSelector, registry entry>. Mirrors `Diamond.functionSelectorRegistry`. */\nexport type SelectorRegistry = Map<string, FunctionSelectorRegistryEntry>;\n\n/**\n * Reserved for M3 upgrade/redeploy reconciliation. Today, prior deployed state is carried\n * by the pre-populated `registry` (entries whose action is `Deployed`). Shape aligned to\n * `DeployedDiamondData.DeployedFacets` to minimise adapter code when M3 wires it in.\n */\nexport type PriorDeployedState = Record<\n\tstring,\n\t{ address?: string; funcSelectors?: string[]; version?: number }\n>;\n\n/**\n * Compute a facet's candidate selectors: its raw ABI selectors **minus `deployExclude`**.\n *\n * `deployInclude` is **additive** (INV-3, M2-E1) — it does NOT reduce the candidate set; a facet keeps\n * its other selectors. The override (force-ownership over a higher-priority facet) and the additive\n * priority resolution both happen in `resolveFunctionSelectorRegistry`. Pure.\n *\n * @param facetSelectors the facet's raw ABI function selectors (4-byte, `0x…`)\n * @param _deployInclude the facet's `deployInclude` signatures — accepted for API symmetry; additive\n * (not used to filter the candidate set)\n * @param deployExclude function signatures to remove\n * @returns the candidate selector list (a new array; the input is not mutated)\n */\nexport function computeFacetSelectors(\n\tfacetSelectors: string[],\n\t_deployInclude: string[],\n\tdeployExclude: string[],\n): string[] {\n\tconst result = [...facetSelectors];\n\n\t// Apply deployExclude (removal) only. deployInclude is additive — it does not filter here.\n\tfor (const excludeSelector of deployExclude) {\n\t\tconst selectorToExclude = ethers.id(excludeSelector).slice(0, 10);\n\t\tconst index = result.indexOf(selectorToExclude);\n\t\tif (index !== -1) {\n\t\t\tresult.splice(index, 1);\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport interface ResolveRegistryArgs {\n\t/** The selector registry to resolve into. Mutated IN PLACE (same contract as before). */\n\tregistry: SelectorRegistry;\n\t/** The newly-deployed facets (with addresses, priorities, include/exclude, funcSelectors). */\n\tnewDeployedFacets: NewDeployedFacets;\n\t/** All facet names currently in the deploy config (used to Remove deleted facets). */\n\tfacetNames: string[];\n\t/**\n\t * Reserved for M3 upgrade/redeploy reconciliation. Unused today — prior deployed state\n\t * is carried by the pre-populated `registry` (entries with action `Deployed`).\n\t */\n\tpriorDeployedState?: PriorDeployedState;\n}\n\n/**\n * Resolve ownership of every function selector across the newly-deployed facets and write\n * the result (`Add`/`Replace`/`Remove`/`Deployed` actions) into `registry` **in place**.\n * Pure — no Hardhat/provider. Extracted verbatim from\n * `BaseDeploymentStrategy.updateFunctionSelectorRegistryTasks`.\n */\nexport function resolveFunctionSelectorRegistry(args: ResolveRegistryArgs): void {\n\tconst { registry, newDeployedFacets, facetNames } = args;\n\tconst zeroAddress = ethers.ZeroAddress;\n\n\tconst newDeployedFacetsByPriority = Object.entries(newDeployedFacets).sort(\n\t\t([, a], [, b]) => (a.priority || 1000) - (b.priority || 1000),\n\t);\n\n\tfor (const [newFacetName, newFacetData] of newDeployedFacetsByPriority) {\n\t\tconst currentFacetAddress = newFacetData.address;\n\t\tconst priority: number = newFacetData.priority;\n\t\tconst includeFuncSelectors: string[] = newFacetData.deployInclude || [];\n\t\tconst excludeFuncSelectors: string[] = newFacetData.deployExclude || [];\n\n\t\t// Convert function signatures to selectors\n\t\tconst includeFuncSelectorsAsSelectors = includeFuncSelectors.map((sig) =>\n\t\t\tethers.id(sig).slice(0, 10),\n\t\t);\n\t\tconst excludeFuncSelectorsAsSelectors = excludeFuncSelectors.map((sig) =>\n\t\t\tethers.id(sig).slice(0, 10),\n\t\t);\n\n\t\t// Initialize funcSelectors if not present\n\t\tif (!newFacetData.funcSelectors) {\n\t\t\tnewFacetData.funcSelectors = [];\n\t\t}\n\t\tconst functionSelectors: string[] = newFacetData.funcSelectors;\n\n\t\t/* ------------------ Exclusion Filter ------------------ */\n\t\tfor (const excludeFuncSelector of excludeFuncSelectorsAsSelectors) {\n\t\t\t// remove from the facets functionSelectors\n\t\t\tif (functionSelectors.includes(excludeFuncSelector)) {\n\t\t\t\tfunctionSelectors.splice(functionSelectors.indexOf(excludeFuncSelector), 1);\n\t\t\t}\n\t\t\t// update action to remove if excluded from registry where a previous deployment associated with facetname\n\t\t\tif (\n\t\t\t\tregistry.has(excludeFuncSelector) &&\n\t\t\t\tregistry.get(excludeFuncSelector)?.facetName === newFacetName\n\t\t\t) {\n\t\t\t\tconst existing = registry.get(excludeFuncSelector);\n\t\t\t\tif (existing?.facetName === newFacetName) {\n\t\t\t\t\tregistry.set(excludeFuncSelector, {\n\t\t\t\t\t\tpriority: priority,\n\t\t\t\t\t\t// EIP-2535: a Remove cut MUST use address(0) (M3-E2 fix for M3-E1 S-3 —\n\t\t\t\t\t\t// was currentFacetAddress, which reverts on-chain on deployExclude-on-upgrade).\n\t\t\t\t\t\taddress: zeroAddress,\n\t\t\t\t\t\taction: RegistryFacetCutAction.Remove,\n\t\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/* ------------------ Inclusion Override Filter ------------------ */\n\t\tfor (const includeFuncSelector of includeFuncSelectorsAsSelectors) {\n\t\t\tconst existingEntry = registry.get(includeFuncSelector);\n\t\t\tif (\n\t\t\t\texistingEntry?.address &&\n\t\t\t\texistingEntry.address !== currentFacetAddress &&\n\t\t\t\texistingEntry.action !== RegistryFacetCutAction.Add\n\t\t\t) {\n\t\t\t\t// Selector already registered/deployed at a different facet address — a selector moved\n\t\t\t\t// facets, or a redeployed facet at a new address (the upgrade case; verified M3-E1 S-1).\n\t\t\t\t// Reconcile -> Replace, not Add (which would revert \"Can't add function that already\n\t\t\t\t// exists\"). Subsumes the removed dead higherPrioritySplit branch (M3-E3).\n\t\t\t\tregistry.set(includeFuncSelector, {\n\t\t\t\t\tpriority: priority,\n\t\t\t\t\taddress: currentFacetAddress,\n\t\t\t\t\taction: RegistryFacetCutAction.Replace,\n\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Add to the registry\n\t\t\t\tregistry.set(includeFuncSelector, {\n\t\t\t\t\tpriority: priority,\n\t\t\t\t\taddress: currentFacetAddress,\n\t\t\t\t\taction: RegistryFacetCutAction.Add,\n\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// remove from the funcSels so it is not modified in Priority Resolution Pass\n\t\t\tconst existing = newDeployedFacets[newFacetName];\n\t\t\tif (\n\t\t\t\texisting &&\n\t\t\t\texisting.funcSelectors &&\n\t\t\t\texisting.funcSelectors.includes(includeFuncSelector)\n\t\t\t) {\n\t\t\t\texisting.funcSelectors.splice(\n\t\t\t\t\texisting.funcSelectors.indexOf(includeFuncSelector),\n\t\t\t\t\t1,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t/* ------------------ Replace Facet and Priority Resolution Pass ------------- */\n\t\tfor (const selector of functionSelectors) {\n\t\t\tconst existing = registry.get(selector);\n\t\t\tif (existing) {\n\t\t\t\tconst existingPriority = existing.priority;\n\n\t\t\t\tif (existing.facetName === newFacetName) {\n\t\t\t\t\t// Same facet, update the address\n\t\t\t\t\tregistry.set(selector, {\n\t\t\t\t\t\tpriority: priority,\n\t\t\t\t\t\taddress: currentFacetAddress,\n\t\t\t\t\t\taction: RegistryFacetCutAction.Replace,\n\t\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t\t});\n\t\t\t\t} else if (priority < existingPriority) {\n\t\t\t\t\t// Current facet has higher priority, Replace it\n\t\t\t\t\tregistry.set(selector, {\n\t\t\t\t\t\tpriority: priority,\n\t\t\t\t\t\taddress: currentFacetAddress,\n\t\t\t\t\t\taction: RegistryFacetCutAction.Replace,\n\t\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// New selector, simply add\n\t\t\t\tregistry.set(selector, {\n\t\t\t\t\tpriority: priority,\n\t\t\t\t\taddress: currentFacetAddress,\n\t\t\t\t\taction: RegistryFacetCutAction.Add,\n\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t/* ---------------- Remove Old Function Selectors from facets -------------- */\n\t\t// Set functionselectors with the newFacetName and still different address to Remove\n\t\tfor (const [selector, entry] of registry.entries()) {\n\t\t\tif (entry.facetName === newFacetName && entry.address !== currentFacetAddress) {\n\t\t\t\tregistry.set(selector, {\n\t\t\t\t\tpriority: entry.priority,\n\t\t\t\t\taddress: zeroAddress,\n\t\t\t\t\taction: RegistryFacetCutAction.Remove,\n\t\t\t\t\tfacetName: newFacetName,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// `Remove` function selectors for facets no longer in config (deleted facets)\n\tfor (const [selector, entry] of registry.entries()) {\n\t\tif (!facetNames.includes(entry.facetName)) {\n\t\t\tregistry.set(selector, {\n\t\t\t\tpriority: entry.priority,\n\t\t\t\taddress: zeroAddress,\n\t\t\t\taction: RegistryFacetCutAction.Remove,\n\t\t\t\tfacetName: entry.facetName,\n\t\t\t});\n\t\t}\n\t}\n}\n"]}
@@ -1,7 +1,7 @@
1
- import "@nomicfoundation/hardhat-ethers";
2
- import { Diamond } from "../core/Diamond";
3
- import { FacetCuts } from "../types";
4
- import { DeploymentStrategy } from "./DeploymentStrategy";
1
+ import '@nomicfoundation/hardhat-ethers';
2
+ import { Diamond } from '../core/Diamond';
3
+ import { FacetCuts } from '../types';
4
+ import { DeploymentStrategy } from './DeploymentStrategy';
5
5
  export declare class BaseDeploymentStrategy implements DeploymentStrategy {
6
6
  protected verbose: boolean;
7
7
  constructor(verbose?: boolean);
@@ -1 +1 @@
1
- {"version":3,"file":"BaseDeploymentStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/BaseDeploymentStrategy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BaseDeploymentStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/BaseDeploymentStrategy.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@ const ethers_1 = require("ethers");
10
10
  const hardhat_1 = __importDefault(require("hardhat"));
11
11
  const types_1 = require("../types");
12
12
  const utils_1 = require("../utils");
13
+ const resolution_1 = require("../resolution");
13
14
  class BaseDeploymentStrategy {
14
15
  verbose;
15
16
  constructor(verbose = false) {
@@ -31,7 +32,7 @@ class BaseDeploymentStrategy {
31
32
  async deployDiamondTasks(diamond) {
32
33
  console.log(chalk_1.default.blueBright(`🚀 Explicitly deploying DiamondCutFacet and Diamond for ${diamond.diamondName}`));
33
34
  // Deploy the DiamondCutFacet - use contract mapping to get correct name
34
- const diamondCutContractName = await (0, utils_1.getContractName)("DiamondCutFacet", diamond);
35
+ const diamondCutContractName = await (0, utils_1.getContractName)('DiamondCutFacet', diamond);
35
36
  const diamondCutFactory = await hardhat_1.default.ethers.getContractFactory(diamondCutContractName, diamond.getSigner());
36
37
  const diamondCutFacet = await diamondCutFactory.deploy();
37
38
  await diamondCutFacet.waitForDeployment();
@@ -51,7 +52,7 @@ class BaseDeploymentStrategy {
51
52
  // Register the DiamondCutFacet function selectors
52
53
  const diamondCutFacetSelectorsRegistry = diamondCutFacetFunctionSelectors.reduce((acc, selector) => {
53
54
  acc[selector] = {
54
- facetName: "DiamondCutFacet",
55
+ facetName: 'DiamondCutFacet',
55
56
  priority: diamond.getFacetsConfig()?.DiamondCutFacet?.priority || 1000, // Default priority if not set
56
57
  address: diamondCutFacetAddress,
57
58
  action: types_1.RegistryFacetCutAction.Deployed,
@@ -64,9 +65,9 @@ class BaseDeploymentStrategy {
64
65
  deployedDiamondData.DeployerAddress = await diamond.getSigner().getAddress();
65
66
  deployedDiamondData.DiamondAddress = diamondContractAddress;
66
67
  deployedDiamondData.DeployedFacets = deployedDiamondData.DeployedFacets || {};
67
- deployedDiamondData.DeployedFacets["DiamondCutFacet"] = {
68
+ deployedDiamondData.DeployedFacets['DiamondCutFacet'] = {
68
69
  address: diamondCutFacetAddress,
69
- tx_hash: diamondCutFacet.deploymentTransaction()?.hash || "",
70
+ tx_hash: diamondCutFacet.deploymentTransaction()?.hash || '',
70
71
  version: 0,
71
72
  funcSelectors: diamondCutFacetFunctionSelectors,
72
73
  };
@@ -101,9 +102,9 @@ class BaseDeploymentStrategy {
101
102
  const deployedDiamondData = diamond.getDeployedDiamondData();
102
103
  const deployedFacets = deployedDiamondData.DeployedFacets || {};
103
104
  const facetCuts = [];
104
- const sortedFacetNames = Object.keys(deployConfig.facets)
105
- .sort((a, b) => {
106
- return (deployConfig.facets[a].priority || 1000) - (deployConfig.facets[b].priority || 1000);
105
+ const sortedFacetNames = Object.keys(deployConfig.facets).sort((a, b) => {
106
+ return ((deployConfig.facets[a].priority || 1000) -
107
+ (deployConfig.facets[b].priority || 1000));
107
108
  });
108
109
  // Save the facet deployment info
109
110
  for (const facetName of sortedFacetNames) {
@@ -111,6 +112,8 @@ class BaseDeploymentStrategy {
111
112
  const deployedVersion = deployedDiamondData.DeployedFacets?.[facetName]?.version ?? -1;
112
113
  const availableVersions = Object.keys(facetConfig.versions || {}).map(Number);
113
114
  const upgradeVersion = Math.max(...availableVersions);
115
+ // Find the original string key that matches the upgrade version number
116
+ const upgradeVersionKey = Object.keys(facetConfig.versions || {}).find((key) => Number(key) === upgradeVersion) || String(upgradeVersion);
114
117
  if (upgradeVersion > deployedVersion || deployedVersion === -1) {
115
118
  if (this.verbose) {
116
119
  console.log(chalk_1.default.blueBright(`🚀 Deploying facet: ${facetName} to version ${upgradeVersion}`));
@@ -118,7 +121,9 @@ class BaseDeploymentStrategy {
118
121
  // Deploy the facet contract - use contract mapping to get correct name
119
122
  const signer = diamond.getSigner();
120
123
  const facetContractName = await (0, utils_1.getContractName)(facetName, diamond);
121
- const facetFactory = await hardhat_1.default.ethers.getContractFactory(facetContractName, { signer });
124
+ const facetFactory = await hardhat_1.default.ethers.getContractFactory(facetContractName, {
125
+ signer,
126
+ });
122
127
  const facetContract = await facetFactory.deploy();
123
128
  await facetContract.waitForDeployment();
124
129
  const deployedFacets = new Map();
@@ -127,9 +132,17 @@ class BaseDeploymentStrategy {
127
132
  facetContract.interface.forEachFunction((func) => {
128
133
  facetSelectors.push(func.selector);
129
134
  });
135
+ // Apply deployExclude (removal) + deployInclude (whitelist) via the pure
136
+ // resolution core. Behavior-preserving (M1-E1); deployInclude stays a
137
+ // whitelist here — M2 makes it additive.
138
+ const excludeFuncSelectors = facetConfig.versions?.[upgradeVersionKey]?.deployExclude || [];
139
+ const includeFuncSelectors = facetConfig.versions?.[upgradeVersionKey]?.deployInclude || [];
140
+ const resolvedFacetSelectors = (0, resolution_1.computeFacetSelectors)(facetSelectors, includeFuncSelectors, excludeFuncSelectors);
141
+ facetSelectors.length = 0;
142
+ facetSelectors.push(...resolvedFacetSelectors);
130
143
  // Initializer function Registry
131
- const deployInit = facetConfig.versions?.[upgradeVersion]?.deployInit || "";
132
- const upgradeInit = facetConfig.versions?.[upgradeVersion]?.upgradeInit || "";
144
+ const deployInit = facetConfig.versions?.[upgradeVersionKey]?.deployInit || '';
145
+ const upgradeInit = facetConfig.versions?.[upgradeVersionKey]?.upgradeInit || '';
133
146
  const initFn = diamond.newDeployment ? deployInit : upgradeInit;
134
147
  if (initFn && facetName !== deployConfig.protocolInitFacet) {
135
148
  diamond.initializerRegistry.set(facetName, initFn);
@@ -137,11 +150,11 @@ class BaseDeploymentStrategy {
137
150
  const newFacetData = {
138
151
  priority: facetConfig.priority || 1000,
139
152
  address: await facetContract.getAddress(),
140
- tx_hash: facetContract.deploymentTransaction()?.hash || "",
153
+ tx_hash: facetContract.deploymentTransaction()?.hash || '',
141
154
  version: upgradeVersion,
142
155
  funcSelectors: facetSelectors,
143
- deployInclude: facetConfig.versions?.[upgradeVersion]?.deployInclude || [],
144
- deployExclude: facetConfig.versions?.[upgradeVersion]?.deployExclude || [],
156
+ deployInclude: facetConfig.versions?.[upgradeVersionKey]?.deployInclude || [],
157
+ deployExclude: facetConfig.versions?.[upgradeVersionKey]?.deployExclude || [],
145
158
  initFunction: initFn,
146
159
  verified: false,
147
160
  };
@@ -176,138 +189,14 @@ class BaseDeploymentStrategy {
176
189
  this.updateFunctionSelectorRegistryTasks(diamond);
177
190
  }
178
191
  async updateFunctionSelectorRegistryTasks(diamond) {
179
- const registry = diamond.functionSelectorRegistry;
180
- const zeroAddress = ethers_1.ethers.ZeroAddress;
181
- const newDeployedFacets = diamond.getNewDeployedFacets();
182
- const newDeployedFacetsByPriority = Object.entries(newDeployedFacets).sort(([, a], [, b]) => (a.priority || 1000) - (b.priority || 1000));
183
- for (const [newFacetName, newFacetData] of newDeployedFacetsByPriority) {
184
- const currentFacetAddress = newFacetData.address;
185
- const priority = newFacetData.priority;
186
- const functionSelectors = newFacetData.funcSelectors || [];
187
- const includeFuncSelectors = newFacetData.deployInclude || [];
188
- const excludeFuncSelectors = newFacetData.deployExclude || [];
189
- /* ------------------ Exclusion Filter ------------------ */
190
- for (const excludeFuncSelector of excludeFuncSelectors) {
191
- // remove from the facets functionSelectors
192
- if (excludeFuncSelector in functionSelectors) {
193
- functionSelectors.splice(functionSelectors.indexOf(excludeFuncSelector), 1);
194
- }
195
- // update action to remove if excluded from registry where a previous deployment associated with facetname
196
- if (excludeFuncSelector in registry && registry.get(excludeFuncSelector)?.facetName === newFacetName) {
197
- const existing = registry.get(excludeFuncSelector);
198
- if (existing && existing.facetName === newFacetName) {
199
- registry.set(excludeFuncSelector, {
200
- priority: priority,
201
- address: currentFacetAddress,
202
- action: types_1.RegistryFacetCutAction.Remove,
203
- facetName: newFacetName,
204
- });
205
- }
206
- }
207
- }
208
- /* ------------ Higher Priority Split of Registry ------------------ */
209
- const registryHigherPrioritySplit = Array.from(registry.entries())
210
- .filter(([_, entry]) => entry.priority > priority)
211
- .reduce((acc, [selector, entry]) => {
212
- if (!acc[entry.facetName]) {
213
- acc[entry.facetName] = [];
214
- }
215
- acc[entry.facetName].push(selector);
216
- return acc;
217
- }, {});
218
- /* ------------------ Inclusion Override Filter ------------------ */
219
- for (const includeFuncSelector of includeFuncSelectors) {
220
- // Force Replace if already registered by higher priority facet
221
- if (includeFuncSelector in registryHigherPrioritySplit) {
222
- const higherPriorityFacet = Object.keys(registryHigherPrioritySplit).find(facetName => {
223
- return registryHigherPrioritySplit[facetName].includes(includeFuncSelector);
224
- });
225
- if (higherPriorityFacet) {
226
- registry.set(includeFuncSelector, {
227
- priority: priority,
228
- address: currentFacetAddress,
229
- action: types_1.RegistryFacetCutAction.Replace,
230
- facetName: newFacetName,
231
- });
232
- }
233
- }
234
- else {
235
- // Add to the registry
236
- registry.set(includeFuncSelector, {
237
- priority: priority,
238
- address: currentFacetAddress,
239
- action: types_1.RegistryFacetCutAction.Add,
240
- facetName: newFacetName,
241
- });
242
- }
243
- // remove from the funcSels so it is not modified in Priority Resolution Pass
244
- if (includeFuncSelector in newDeployedFacets) {
245
- const existing = newDeployedFacets[newFacetName];
246
- if (existing && existing.funcSelectors) {
247
- existing.funcSelectors.splice(existing.funcSelectors.indexOf(includeFuncSelector), 1);
248
- }
249
- }
250
- }
251
- /* ------------------ Replace Facet and Priority Resolution Pass ------------- */
252
- for (const selector of functionSelectors) {
253
- const existing = registry.get(selector);
254
- if (existing) {
255
- const existingPriority = existing.priority;
256
- if (existing.facetName === newFacetName) {
257
- // Same facet, update the address
258
- registry.set(selector, {
259
- priority: priority,
260
- address: currentFacetAddress,
261
- action: types_1.RegistryFacetCutAction.Replace,
262
- facetName: newFacetName,
263
- });
264
- }
265
- else if (priority < existingPriority) {
266
- // Current facet has higher priority, Replace it
267
- registry.set(selector, {
268
- priority: priority,
269
- address: currentFacetAddress,
270
- action: types_1.RegistryFacetCutAction.Replace,
271
- facetName: newFacetName,
272
- });
273
- }
274
- }
275
- else {
276
- // New selector, simply add
277
- registry.set(selector, {
278
- priority: priority,
279
- address: currentFacetAddress,
280
- action: types_1.RegistryFacetCutAction.Add,
281
- facetName: newFacetName,
282
- });
283
- }
284
- }
285
- /* ---------------- Remove Old Function Selectors from facets -------------- */
286
- // Set functionselectors with the newFacetName and still different address to Remove
287
- for (const [selector, entry] of registry.entries()) {
288
- if (entry.facetName === newFacetName && entry.address !== currentFacetAddress) {
289
- registry.set(selector, {
290
- priority: entry.priority,
291
- address: zeroAddress,
292
- action: types_1.RegistryFacetCutAction.Remove,
293
- facetName: newFacetName,
294
- });
295
- }
296
- }
297
- }
298
- // `Remove` function selectors for facets no longer in config (deleted facets)
299
- const facetsConfig = diamond.getDeployConfig().facets;
300
- const facetNames = Object.keys(facetsConfig);
301
- for (const [selector, entry] of registry.entries()) {
302
- if (!facetNames.includes(entry.facetName)) {
303
- registry.set(selector, {
304
- priority: entry.priority,
305
- address: zeroAddress,
306
- action: types_1.RegistryFacetCutAction.Remove,
307
- facetName: entry.facetName,
308
- });
309
- }
310
- }
192
+ // Delegates to the pure resolution core (M1-E1). Behavior-preserving: the dead
193
+ // `higherPrioritySplit` and the `5b2f7af` Replace branch live there verbatim and are
194
+ // reconciled in M3. The registry Map is mutated in place, exactly as before.
195
+ (0, resolution_1.resolveFunctionSelectorRegistry)({
196
+ registry: diamond.functionSelectorRegistry,
197
+ newDeployedFacets: diamond.getNewDeployedFacets(),
198
+ facetNames: Object.keys(diamond.getDeployConfig().facets),
199
+ });
311
200
  }
312
201
  async postUpdateFunctionSelectorRegistry(diamond) {
313
202
  if (this.verbose) {
@@ -335,13 +224,13 @@ class BaseDeploymentStrategy {
335
224
  async performDiamondCutTasks(diamond) {
336
225
  const diamondSignerAddress = await diamond.getSigner()?.getAddress();
337
226
  const signer = await hardhat_1.default.ethers.getSigner(diamondSignerAddress);
338
- const diamondContract = await hardhat_1.default.ethers.getContractAt("IDiamondCut", diamond.getDeployedDiamondData().DiamondAddress);
227
+ const diamondContract = await hardhat_1.default.ethers.getContractAt('IDiamondCut', diamond.getDeployedDiamondData().DiamondAddress);
339
228
  const signerDiamondContract = diamondContract.connect(signer);
340
229
  const deployConfig = diamond.getDeployConfig();
341
230
  const deployedDiamondData = diamond.getDeployedDiamondData();
342
231
  // Setup initCallData with Atomic Protocol Initializer
343
232
  const [initCalldata, initAddress] = await this.getInitCalldata(diamond);
344
- // extract facet cuts from the selector registry
233
+ // extract facet cuts from the selector registry
345
234
  const facetCuts = await this.getFacetCuts(diamond);
346
235
  // Validate no orphaned selectors, i.e. 'Add', 'Replace' or 'Deployed' selectors with the same facetNames but different addresses
347
236
  await this.validateNoOrphanedSelectors(facetCuts);
@@ -356,7 +245,11 @@ class BaseDeploymentStrategy {
356
245
  }
357
246
  }
358
247
  /* -------------------------- Perform the diamond cut -----------------------*/
359
- const facetSelectorCutMap = facetCuts.map(fc => ({ facetAddress: fc.facetAddress, action: fc.action, functionSelectors: fc.functionSelectors }));
248
+ const facetSelectorCutMap = facetCuts.map((fc) => ({
249
+ facetAddress: fc.facetAddress,
250
+ action: fc.action,
251
+ functionSelectors: fc.functionSelectors,
252
+ }));
360
253
  const tx = await signerDiamondContract.diamondCut(facetSelectorCutMap, initAddress, initCalldata);
361
254
  /* --------------------- Update the deployed diamond data ------------------ */
362
255
  const txHash = tx.hash;
@@ -364,7 +257,7 @@ class BaseDeploymentStrategy {
364
257
  const ifaceList = (0, utils_1.getDeployedFacetInterfaces)(deployedDiamondData);
365
258
  // Log the transaction
366
259
  if (this.verbose) {
367
- await (0, utils_1.logTx)(tx, "DiamondCut", ifaceList);
260
+ await (0, utils_1.logTx)(tx, 'DiamondCut', ifaceList);
368
261
  }
369
262
  else {
370
263
  console.log(chalk_1.default.blueBright(`🔄 Waiting for DiamondCut transaction to be mined...`));
@@ -400,13 +293,15 @@ class BaseDeploymentStrategy {
400
293
  const deployedDiamondData = diamond.getDeployedDiamondData();
401
294
  const deployConfig = diamond.getDeployConfig();
402
295
  let initAddress = ethers_1.ethers.ZeroAddress;
403
- let initCalldata = "0x";
404
- const protocolInitFacet = deployConfig.protocolInitFacet || "";
296
+ let initCalldata = '0x';
297
+ const protocolInitFacet = deployConfig.protocolInitFacet || '';
405
298
  const protocolVersion = deployConfig.protocolVersion;
406
299
  const protocolFacetInfo = diamond.getNewDeployedFacets()[protocolInitFacet];
407
300
  if (protocolInitFacet && protocolFacetInfo) {
408
301
  const versionCfg = deployConfig.facets[protocolInitFacet]?.versions?.[protocolVersion];
409
- const initFn = diamond.newDeployment ? versionCfg?.deployInit : versionCfg?.upgradeInit;
302
+ const initFn = diamond.newDeployment
303
+ ? versionCfg?.deployInit
304
+ : versionCfg?.upgradeInit;
410
305
  if (initFn) {
411
306
  const iface = new ethers_1.ethers.Interface([`function ${initFn}`]);
412
307
  initAddress = protocolFacetInfo.address;
@@ -426,7 +321,7 @@ class BaseDeploymentStrategy {
426
321
  const deployConfig = diamond.getDeployConfig();
427
322
  const selectorRegistry = diamond.functionSelectorRegistry;
428
323
  /* -------------------------- Prepare the facet cuts -----------------------*/
429
- // extract facet cuts from the selector registry
324
+ // extract facet cuts from the selector registry
430
325
  const facetCuts = Array.from(selectorRegistry.entries())
431
326
  .filter(([_, entry]) => entry.action !== types_1.RegistryFacetCutAction.Deployed)
432
327
  .map(([selector, entry]) => {
@@ -441,8 +336,8 @@ class BaseDeploymentStrategy {
441
336
  }
442
337
  async validateNoOrphanedSelectors(facetCuts) {
443
338
  // Validate no orphaned selectors, i.e. 'Add', 'Replace' or 'Deployed' selectors with the same facetNames but different addresses
444
- const orphanedSelectors = facetCuts.filter(facetCut => {
445
- return facetCuts.some(otherFacetCut => {
339
+ const orphanedSelectors = facetCuts.filter((facetCut) => {
340
+ return facetCuts.some((otherFacetCut) => {
446
341
  return (otherFacetCut.facetAddress !== facetCut.facetAddress &&
447
342
  otherFacetCut.name === facetCut.name &&
448
343
  (otherFacetCut.action === types_1.RegistryFacetCutAction.Add ||
@@ -469,7 +364,9 @@ class BaseDeploymentStrategy {
469
364
  if (!facetSelectorsMap[facetName]) {
470
365
  facetSelectorsMap[facetName] = { address: entry.address, selectors: [] };
471
366
  }
472
- if (entry.action === types_1.RegistryFacetCutAction.Add || entry.action === types_1.RegistryFacetCutAction.Replace || entry.action === types_1.RegistryFacetCutAction.Deployed) {
367
+ if (entry.action === types_1.RegistryFacetCutAction.Add ||
368
+ entry.action === types_1.RegistryFacetCutAction.Replace ||
369
+ entry.action === types_1.RegistryFacetCutAction.Deployed) {
473
370
  facetSelectorsMap[facetName].selectors.push(selector);
474
371
  }
475
372
  }
@@ -497,7 +394,7 @@ class BaseDeploymentStrategy {
497
394
  }
498
395
  // Remove facets with no selectors
499
396
  for (const facetName of Object.keys(deployedDiamondData.DeployedFacets)) {
500
- if (!facetSelectorsMap[facetName] || facetSelectorsMap[facetName].selectors.length === 0) {
397
+ if (facetSelectorsMap[facetName]?.selectors.length === 0) {
501
398
  delete deployedDiamondData.DeployedFacets[facetName];
502
399
  }
503
400
  }