@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.
- package/README.md +618 -0
- package/diamonds/README.md +3 -0
- package/dist/core/CallbackManager.d.ts +13 -0
- package/dist/core/CallbackManager.d.ts.map +1 -0
- package/dist/core/CallbackManager.js +95 -0
- package/dist/core/CallbackManager.js.map +1 -0
- package/dist/core/DeploymentManager.d.ts +10 -0
- package/dist/core/DeploymentManager.d.ts.map +1 -0
- package/dist/core/DeploymentManager.js +50 -0
- package/dist/core/DeploymentManager.js.map +1 -0
- package/dist/core/Diamond.d.ts +58 -0
- package/dist/core/Diamond.d.ts.map +1 -0
- package/dist/core/Diamond.js +146 -0
- package/dist/core/Diamond.js.map +1 -0
- package/dist/core/DiamondDeployer.d.ts +10 -0
- package/dist/core/DiamondDeployer.d.ts.map +1 -0
- package/dist/core/DiamondDeployer.js +33 -0
- package/dist/core/DiamondDeployer.js.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +12 -0
- package/dist/core/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/repositories/DBDeploymentRepository.d.ts +1 -0
- package/dist/repositories/DBDeploymentRepository.d.ts.map +1 -0
- package/dist/repositories/DBDeploymentRepository.js +20 -0
- package/dist/repositories/DBDeploymentRepository.js.map +1 -0
- package/dist/repositories/DeploymentRepository.d.ts +8 -0
- package/dist/repositories/DeploymentRepository.d.ts.map +1 -0
- package/dist/repositories/DeploymentRepository.js +7 -0
- package/dist/repositories/DeploymentRepository.js.map +1 -0
- package/dist/repositories/FileDeploymentRepository.d.ts +18 -0
- package/dist/repositories/FileDeploymentRepository.d.ts.map +1 -0
- package/dist/repositories/FileDeploymentRepository.js +58 -0
- package/dist/repositories/FileDeploymentRepository.js.map +1 -0
- package/dist/repositories/databaseHandler.d.ts +1 -0
- package/dist/repositories/databaseHandler.d.ts.map +1 -0
- package/dist/repositories/databaseHandler.js +13 -0
- package/dist/repositories/databaseHandler.js.map +1 -0
- package/dist/repositories/index.d.ts +4 -0
- package/dist/repositories/index.d.ts.map +1 -0
- package/dist/repositories/index.js +20 -0
- package/dist/repositories/index.js.map +1 -0
- package/dist/repositories/jsonFileHandler.d.ts +81 -0
- package/dist/repositories/jsonFileHandler.d.ts.map +1 -0
- package/dist/repositories/jsonFileHandler.js +223 -0
- package/dist/repositories/jsonFileHandler.js.map +1 -0
- package/dist/repositories/prismaDBHandler.d.ts +1 -0
- package/dist/repositories/prismaDBHandler.d.ts.map +1 -0
- package/dist/repositories/prismaDBHandler.js +11 -0
- package/dist/repositories/prismaDBHandler.js.map +1 -0
- package/dist/schemas/DeploymentSchema.d.ts +309 -0
- package/dist/schemas/DeploymentSchema.d.ts.map +1 -0
- package/dist/schemas/DeploymentSchema.js +56 -0
- package/dist/schemas/DeploymentSchema.js.map +1 -0
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +18 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/strategies/BaseDeploymentStrategy.d.ts +41 -0
- package/dist/strategies/BaseDeploymentStrategy.d.ts.map +1 -0
- package/dist/strategies/BaseDeploymentStrategy.js +545 -0
- package/dist/strategies/BaseDeploymentStrategy.js.map +1 -0
- package/dist/strategies/DeploymentStrategy.d.ts +19 -0
- package/dist/strategies/DeploymentStrategy.d.ts.map +1 -0
- package/dist/strategies/DeploymentStrategy.js +3 -0
- package/dist/strategies/DeploymentStrategy.js.map +1 -0
- package/dist/strategies/LocalDeploymentStrategy.d.ts +4 -0
- package/dist/strategies/LocalDeploymentStrategy.d.ts.map +1 -0
- package/dist/strategies/LocalDeploymentStrategy.js +8 -0
- package/dist/strategies/LocalDeploymentStrategy.js.map +1 -0
- package/dist/strategies/OZDefenderDeploymentStrategy.d.ts +62 -0
- package/dist/strategies/OZDefenderDeploymentStrategy.d.ts.map +1 -0
- package/dist/strategies/OZDefenderDeploymentStrategy.js +757 -0
- package/dist/strategies/OZDefenderDeploymentStrategy.js.map +1 -0
- package/dist/strategies/RPCDeploymentStrategy.d.ts +139 -0
- package/dist/strategies/RPCDeploymentStrategy.d.ts.map +1 -0
- package/dist/strategies/RPCDeploymentStrategy.js +710 -0
- package/dist/strategies/RPCDeploymentStrategy.js.map +1 -0
- package/dist/strategies/index.d.ts +6 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +12 -0
- package/dist/strategies/index.js.map +1 -0
- package/dist/types/config.d.ts +26 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/defender.d.ts +22 -0
- package/dist/types/defender.d.ts.map +1 -0
- package/dist/types/defender.js +3 -0
- package/dist/types/defender.js.map +1 -0
- package/dist/types/deployments.d.ts +71 -0
- package/dist/types/deployments.d.ts.map +1 -0
- package/dist/types/deployments.js +20 -0
- package/dist/types/deployments.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +21 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/rpc.d.ts +35 -0
- package/dist/types/rpc.d.ts.map +1 -0
- package/dist/types/rpc.js +3 -0
- package/dist/types/rpc.js.map +1 -0
- package/dist/utils/common.d.ts +20 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/common.js +45 -0
- package/dist/utils/common.js.map +1 -0
- package/dist/utils/configurationResolver.d.ts +30 -0
- package/dist/utils/configurationResolver.d.ts.map +1 -0
- package/dist/utils/configurationResolver.js +151 -0
- package/dist/utils/configurationResolver.js.map +1 -0
- package/dist/utils/contractMapping.d.ts +29 -0
- package/dist/utils/contractMapping.d.ts.map +1 -0
- package/dist/utils/contractMapping.js +224 -0
- package/dist/utils/contractMapping.js.map +1 -0
- package/dist/utils/defenderClients.d.ts +5 -0
- package/dist/utils/defenderClients.d.ts.map +1 -0
- package/dist/utils/defenderClients.js +21 -0
- package/dist/utils/defenderClients.js.map +1 -0
- package/dist/utils/defenderStore.d.ts +14 -0
- package/dist/utils/defenderStore.d.ts.map +1 -0
- package/dist/utils/defenderStore.js +92 -0
- package/dist/utils/defenderStore.js.map +1 -0
- package/dist/utils/diamondAbiGenerator.d.ts +113 -0
- package/dist/utils/diamondAbiGenerator.d.ts.map +1 -0
- package/dist/utils/diamondAbiGenerator.js +415 -0
- package/dist/utils/diamondAbiGenerator.js.map +1 -0
- package/dist/utils/diffDeployedFacets.d.ts +26 -0
- package/dist/utils/diffDeployedFacets.d.ts.map +1 -0
- package/dist/utils/diffDeployedFacets.js +106 -0
- package/dist/utils/diffDeployedFacets.js.map +1 -0
- package/dist/utils/index.d.ts +16 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +35 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/loupe.d.ts +44 -0
- package/dist/utils/loupe.d.ts.map +1 -0
- package/dist/utils/loupe.js +128 -0
- package/dist/utils/loupe.js.map +1 -0
- package/dist/utils/rpcStore.d.ts +36 -0
- package/dist/utils/rpcStore.d.ts.map +1 -0
- package/dist/utils/rpcStore.js +166 -0
- package/dist/utils/rpcStore.js.map +1 -0
- package/dist/utils/signer.d.ts +36 -0
- package/dist/utils/signer.d.ts.map +1 -0
- package/dist/utils/signer.js +91 -0
- package/dist/utils/signer.js.map +1 -0
- package/dist/utils/txlogging.d.ts +13 -0
- package/dist/utils/txlogging.d.ts.map +1 -0
- package/dist/utils/txlogging.js +87 -0
- package/dist/utils/txlogging.js.map +1 -0
- package/dist/utils/workspaceSetup.d.ts +32 -0
- package/dist/utils/workspaceSetup.d.ts.map +1 -0
- package/dist/utils/workspaceSetup.js +311 -0
- package/dist/utils/workspaceSetup.js.map +1 -0
- package/docs/DIAMOND_ABI_CONFIGURATION_SUMMARY.md +40 -0
- package/docs/DIAMOND_ABI_GENERATION.md +220 -0
- package/docs/DIAMOND_ABI_GENERATOR_EXAMPLES.md +1204 -0
- package/docs/DIAMOND_ABI_GENERATOR_IMPLEMENTATION.md +947 -0
- package/docs/DIAMOND_ABI_GENERATOR_QUICK_REFERENCE.md +336 -0
- package/docs/README-DEFENDER.md +394 -0
- package/docs/README_DIAMOND_ABI_GENERATOR.md +303 -0
- package/docs/ROADMAP.md +250 -0
- package/docs/assets/image.png +0 -0
- package/docs/defender-integration.md +451 -0
- package/docs/diamond_module-BaseStrategy_design-v2.uxf +247 -0
- package/docs/diamond_module-BaseStrategy_design.uxf +272 -0
- package/docs/monitoring-troubleshooting.md +556 -0
- package/docs/testing-guide.md +713 -0
- package/examples/Diamond_Config_and_Deployment_examples/diamonds/ProxyDiamond/callbacks/ERC20ProxyFacet.ts +31 -0
- package/examples/Diamond_Config_and_Deployment_examples/diamonds/ProxyDiamond/proxydiamond.config.json +27 -0
- package/examples/Local_Hardhat_Deployer_Script_example/LocalDiamondDeployer.ts +180 -0
- package/examples/OZ_Defender_Deployer_Script_example/OZDiamondDeployer.ts +107 -0
- package/examples/OZ_Defender_Deployer_Script_example/run-oz-deploy.ts +17 -0
- package/examples/Test_examples/ProxyDiamondDeployment.test.ts +202 -0
- package/examples/defender-deployment/.env.example +35 -0
- package/examples/defender-deployment/README.md +415 -0
- package/examples/defender-deployment/contracts/ExampleDiamond.sol +41 -0
- package/examples/defender-deployment/contracts/ExampleFacet1.sol +84 -0
- package/examples/defender-deployment/contracts/ExampleFacet2.sol +104 -0
- package/examples/defender-deployment/contracts/UpgradeFacet.sol +92 -0
- package/examples/defender-deployment/deploy-script.ts +170 -0
- package/examples/defender-deployment/diamond-config.json +36 -0
- package/examples/defender-deployment/upgrade-script.ts +237 -0
- package/examples/hardhat-diamonds-config.example.ts +41 -0
- package/package.json +228 -0
- package/src/core/CallbackManager.ts +70 -0
- package/src/core/DeploymentManager.ts +64 -0
- package/src/core/Diamond.ts +197 -0
- package/src/core/DiamondDeployer.ts +36 -0
- package/src/core/index.ts +4 -0
- package/src/index.ts +5 -0
- package/src/repositories/DBDeploymentRepository.ts +22 -0
- package/src/repositories/DeploymentRepository.ts +12 -0
- package/src/repositories/FileDeploymentRepository.ts +67 -0
- package/src/repositories/databaseHandler.ts +14 -0
- package/src/repositories/index.ts +4 -0
- package/src/repositories/jsonFileHandler.ts +252 -0
- package/src/repositories/prismaDBHandler.ts +10 -0
- package/src/schemas/DeploymentSchema.ts +71 -0
- package/src/schemas/index.ts +1 -0
- package/src/strategies/BaseDeploymentStrategy.ts +649 -0
- package/src/strategies/DeploymentStrategy.ts +25 -0
- package/src/strategies/LocalDeploymentStrategy.ts +5 -0
- package/src/strategies/OZDefenderDeploymentStrategy.ts +849 -0
- package/src/strategies/RPCDeploymentStrategy.ts +881 -0
- package/src/strategies/index.ts +5 -0
- package/src/types/config.ts +34 -0
- package/src/types/defender.ts +24 -0
- package/src/types/deployments.ts +102 -0
- package/src/types/index.ts +4 -0
- package/src/types/rpc.ts +37 -0
- package/src/utils/common.ts +54 -0
- package/src/utils/configurationResolver.ts +141 -0
- package/src/utils/contractMapping.ts +220 -0
- package/src/utils/defenderClients.ts +22 -0
- package/src/utils/defenderStore.ts +62 -0
- package/src/utils/diamondAbiGenerator.ts +523 -0
- package/src/utils/diffDeployedFacets.ts +131 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/loupe.ts +159 -0
- package/src/utils/rpcStore.ts +152 -0
- package/src/utils/signer.ts +93 -0
- package/src/utils/txlogging.ts +97 -0
- package/src/utils/workspaceSetup.ts +315 -0
- 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
|
+
}
|