@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,1204 @@
|
|
|
1
|
+
# Diamond ABI Generator - Practical Examples & Patterns
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document provides practical examples and common patterns for implementing the Diamond ABI Generator in real-world projects. It complements the main implementation guide with focused examples for specific use cases.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Common Integration Patterns](#common-integration-patterns)
|
|
10
|
+
2. [Frontend Framework Integration](#frontend-framework-integration)
|
|
11
|
+
3. [Testing Patterns](#testing-patterns)
|
|
12
|
+
4. [CI/CD Integration](#cicd-integration)
|
|
13
|
+
5. [Performance Optimization](#performance-optimization)
|
|
14
|
+
6. [Custom Tooling](#custom-tooling)
|
|
15
|
+
7. [Migration Strategies](#migration-strategies)
|
|
16
|
+
|
|
17
|
+
## Common Integration Patterns
|
|
18
|
+
|
|
19
|
+
### Pattern 1: Basic Project Setup
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// scripts/setup-diamond-abi.ts
|
|
23
|
+
import { generateDiamondAbi, Diamond } from 'diamonds';
|
|
24
|
+
import { FileDeploymentRepository } from 'diamonds/repositories';
|
|
25
|
+
import { writeFileSync, mkdirSync } from 'fs';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
|
|
28
|
+
interface ProjectConfig {
|
|
29
|
+
diamondName: string;
|
|
30
|
+
networkName: string;
|
|
31
|
+
chainId: number;
|
|
32
|
+
outputDir: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class DiamondAbiManager {
|
|
36
|
+
private diamond: Diamond;
|
|
37
|
+
private config: ProjectConfig;
|
|
38
|
+
|
|
39
|
+
constructor(config: ProjectConfig) {
|
|
40
|
+
this.config = config;
|
|
41
|
+
|
|
42
|
+
const diamondConfig = {
|
|
43
|
+
diamondName: config.diamondName,
|
|
44
|
+
networkName: config.networkName,
|
|
45
|
+
chainId: config.chainId,
|
|
46
|
+
deploymentsPath: './diamonds',
|
|
47
|
+
contractsPath: './contracts'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const repository = new FileDeploymentRepository(diamondConfig);
|
|
51
|
+
this.diamond = new Diamond(diamondConfig, repository);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async generateAbi() {
|
|
55
|
+
const result = await generateDiamondAbi(this.diamond, {
|
|
56
|
+
outputDir: this.config.outputDir,
|
|
57
|
+
includeSourceInfo: true,
|
|
58
|
+
validateSelectors: true,
|
|
59
|
+
verbose: true
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async generateTypes() {
|
|
66
|
+
const result = await this.generateAbi();
|
|
67
|
+
|
|
68
|
+
// Generate additional type files
|
|
69
|
+
await this.generateViemTypes(result);
|
|
70
|
+
await this.generateReactTypes(result);
|
|
71
|
+
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async generateViemTypes(result: any) {
|
|
76
|
+
const viemTypes = `
|
|
77
|
+
// Auto-generated Viem types for ${this.config.diamondName}
|
|
78
|
+
import { Abi } from 'viem';
|
|
79
|
+
|
|
80
|
+
export const ${this.config.diamondName}ABI = ${JSON.stringify(result.abi, null, 2)} as const;
|
|
81
|
+
export type ${this.config.diamondName}ABI = typeof ${this.config.diamondName}ABI;
|
|
82
|
+
|
|
83
|
+
// Selector mapping
|
|
84
|
+
export const selectorMap = ${JSON.stringify(result.selectorMap, null, 2)} as const;
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const outputPath = join(this.config.outputDir, 'viem-types.ts');
|
|
88
|
+
writeFileSync(outputPath, viemTypes);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private async generateReactTypes(result: any) {
|
|
92
|
+
const reactTypes = `
|
|
93
|
+
// Auto-generated React types for ${this.config.diamondName}
|
|
94
|
+
import { Contract } from 'ethers';
|
|
95
|
+
|
|
96
|
+
export interface ${this.config.diamondName}Contract extends Contract {
|
|
97
|
+
// Add your specific function signatures here
|
|
98
|
+
// These would be generated from the ABI
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const ${this.config.diamondName}ABI = ${JSON.stringify(result.abi, null, 2)};
|
|
102
|
+
`;
|
|
103
|
+
|
|
104
|
+
const outputPath = join(this.config.outputDir, 'react-types.ts');
|
|
105
|
+
writeFileSync(outputPath, reactTypes);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Usage
|
|
110
|
+
const manager = new DiamondAbiManager({
|
|
111
|
+
diamondName: 'GameDiamond',
|
|
112
|
+
networkName: 'localhost',
|
|
113
|
+
chainId: 31337,
|
|
114
|
+
outputDir: './src/contracts'
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
manager.generateTypes().then(() => {
|
|
118
|
+
console.log('ā
Diamond ABI and types generated');
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Pattern 2: Multi-Network Support
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// scripts/multi-network-abi.ts
|
|
126
|
+
import { DiamondAbiManager } from './setup-diamond-abi';
|
|
127
|
+
|
|
128
|
+
const networks = [
|
|
129
|
+
{ name: 'localhost', chainId: 31337 },
|
|
130
|
+
{ name: 'sepolia', chainId: 11155111 },
|
|
131
|
+
{ name: 'mainnet', chainId: 1 }
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
const diamonds = ['GameDiamond', 'TradingDiamond', 'GovernanceDiamond'];
|
|
135
|
+
|
|
136
|
+
async function generateAllAbis() {
|
|
137
|
+
for (const network of networks) {
|
|
138
|
+
for (const diamondName of diamonds) {
|
|
139
|
+
try {
|
|
140
|
+
const manager = new DiamondAbiManager({
|
|
141
|
+
diamondName,
|
|
142
|
+
networkName: network.name,
|
|
143
|
+
chainId: network.chainId,
|
|
144
|
+
outputDir: `./src/contracts/${network.name}`
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
await manager.generateTypes();
|
|
148
|
+
console.log(`ā
Generated ABI for ${diamondName} on ${network.name}`);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error(`ā Failed to generate ABI for ${diamondName} on ${network.name}:`, error);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
generateAllAbis();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Pattern 3: Watch Mode for Development
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// scripts/watch-diamond-abi.ts
|
|
163
|
+
import { watch } from 'fs';
|
|
164
|
+
import { DiamondAbiManager } from './setup-diamond-abi';
|
|
165
|
+
import { debounce } from 'lodash';
|
|
166
|
+
|
|
167
|
+
class DiamondAbiWatcher {
|
|
168
|
+
private manager: DiamondAbiManager;
|
|
169
|
+
private regenerateAbi: () => Promise<void>;
|
|
170
|
+
|
|
171
|
+
constructor(manager: DiamondAbiManager) {
|
|
172
|
+
this.manager = manager;
|
|
173
|
+
this.regenerateAbi = debounce(async () => {
|
|
174
|
+
try {
|
|
175
|
+
console.log('š Regenerating Diamond ABI...');
|
|
176
|
+
await this.manager.generateTypes();
|
|
177
|
+
console.log('ā
Diamond ABI regenerated');
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error('ā Failed to regenerate ABI:', error);
|
|
180
|
+
}
|
|
181
|
+
}, 1000);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
start() {
|
|
185
|
+
// Watch for contract changes
|
|
186
|
+
watch('./contracts', { recursive: true }, (eventType, filename) => {
|
|
187
|
+
if (filename?.endsWith('.sol')) {
|
|
188
|
+
console.log(`š Contract changed: ${filename}`);
|
|
189
|
+
this.regenerateAbi();
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// Watch for diamond deployment changes
|
|
194
|
+
watch('./diamonds', { recursive: true }, (eventType, filename) => {
|
|
195
|
+
if (filename?.endsWith('.json')) {
|
|
196
|
+
console.log(`š Diamond deployment changed: ${filename}`);
|
|
197
|
+
this.regenerateAbi();
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
console.log('š Watching for changes...');
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Usage
|
|
206
|
+
const manager = new DiamondAbiManager({
|
|
207
|
+
diamondName: 'GameDiamond',
|
|
208
|
+
networkName: 'localhost',
|
|
209
|
+
chainId: 31337,
|
|
210
|
+
outputDir: './src/contracts'
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const watcher = new DiamondAbiWatcher(manager);
|
|
214
|
+
watcher.start();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Frontend Framework Integration
|
|
218
|
+
|
|
219
|
+
### React + Ethers.js Integration
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
// src/hooks/useDiamond.ts
|
|
223
|
+
import { useEffect, useState } from 'react';
|
|
224
|
+
import { ethers } from 'ethers';
|
|
225
|
+
import { GameDiamondABI } from '../contracts/GameDiamond';
|
|
226
|
+
|
|
227
|
+
export function useDiamond(address: string, provider: ethers.providers.Provider) {
|
|
228
|
+
const [contract, setContract] = useState<ethers.Contract | null>(null);
|
|
229
|
+
const [loading, setLoading] = useState(true);
|
|
230
|
+
const [error, setError] = useState<string | null>(null);
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
async function setupContract() {
|
|
234
|
+
try {
|
|
235
|
+
setLoading(true);
|
|
236
|
+
const diamondContract = new ethers.Contract(
|
|
237
|
+
address,
|
|
238
|
+
GameDiamondABI.abi,
|
|
239
|
+
provider
|
|
240
|
+
);
|
|
241
|
+
setContract(diamondContract);
|
|
242
|
+
setError(null);
|
|
243
|
+
} catch (err) {
|
|
244
|
+
setError(err instanceof Error ? err.message : 'Failed to setup contract');
|
|
245
|
+
} finally {
|
|
246
|
+
setLoading(false);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
setupContract();
|
|
251
|
+
}, [address, provider]);
|
|
252
|
+
|
|
253
|
+
return { contract, loading, error };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Usage component
|
|
257
|
+
export function GameComponent() {
|
|
258
|
+
const { contract, loading, error } = useDiamond(
|
|
259
|
+
'0x...',
|
|
260
|
+
new ethers.providers.Web3Provider(window.ethereum)
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
if (loading) return <div>Loading...</div>;
|
|
264
|
+
if (error) return <div>Error: {error}</div>;
|
|
265
|
+
|
|
266
|
+
return (
|
|
267
|
+
<div>
|
|
268
|
+
<button onClick={() => contract?.someFunction()}>
|
|
269
|
+
Call Function
|
|
270
|
+
</button>
|
|
271
|
+
</div>
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
### Next.js Integration
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// next.config.js
|
|
280
|
+
/** @type {import('next').NextConfig} */
|
|
281
|
+
const nextConfig = {
|
|
282
|
+
webpack: (config) => {
|
|
283
|
+
// Handle JSON imports for ABI files
|
|
284
|
+
config.module.rules.push({
|
|
285
|
+
test: /\.json$/,
|
|
286
|
+
type: 'json'
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
return config;
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
module.exports = nextConfig;
|
|
294
|
+
|
|
295
|
+
// pages/api/diamond-abi.ts
|
|
296
|
+
import { NextApiRequest, NextApiResponse } from 'next';
|
|
297
|
+
import { generateDiamondAbi } from 'diamonds';
|
|
298
|
+
|
|
299
|
+
export default async function handler(
|
|
300
|
+
req: NextApiRequest,
|
|
301
|
+
res: NextApiResponse
|
|
302
|
+
) {
|
|
303
|
+
if (req.method !== 'GET') {
|
|
304
|
+
return res.status(405).json({ message: 'Method not allowed' });
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
const { diamondName, network } = req.query;
|
|
309
|
+
|
|
310
|
+
// Generate ABI on-demand
|
|
311
|
+
const result = await generateDiamondAbi(diamond, {
|
|
312
|
+
verbose: false
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
res.status(200).json({
|
|
316
|
+
abi: result.abi,
|
|
317
|
+
selectorMap: result.selectorMap,
|
|
318
|
+
stats: result.stats
|
|
319
|
+
});
|
|
320
|
+
} catch (error) {
|
|
321
|
+
res.status(500).json({
|
|
322
|
+
message: 'Failed to generate ABI',
|
|
323
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Vue.js Integration
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// src/composables/useDiamond.ts
|
|
333
|
+
import { ref, computed } from 'vue';
|
|
334
|
+
import { ethers } from 'ethers';
|
|
335
|
+
import { GameDiamondABI } from '../contracts/GameDiamond';
|
|
336
|
+
|
|
337
|
+
export function useDiamond(address: string) {
|
|
338
|
+
const contract = ref<ethers.Contract | null>(null);
|
|
339
|
+
const loading = ref(false);
|
|
340
|
+
const error = ref<string | null>(null);
|
|
341
|
+
|
|
342
|
+
const isReady = computed(() => contract.value !== null);
|
|
343
|
+
|
|
344
|
+
async function connect(provider: ethers.providers.Provider) {
|
|
345
|
+
loading.value = true;
|
|
346
|
+
error.value = null;
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
contract.value = new ethers.Contract(
|
|
350
|
+
address,
|
|
351
|
+
GameDiamondABI.abi,
|
|
352
|
+
provider
|
|
353
|
+
);
|
|
354
|
+
} catch (err) {
|
|
355
|
+
error.value = err instanceof Error ? err.message : 'Failed to connect';
|
|
356
|
+
} finally {
|
|
357
|
+
loading.value = false;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async function callFunction(functionName: string, ...args: any[]) {
|
|
362
|
+
if (!contract.value) {
|
|
363
|
+
throw new Error('Contract not connected');
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return await contract.value[functionName](...args);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return {
|
|
370
|
+
contract: computed(() => contract.value),
|
|
371
|
+
loading: computed(() => loading.value),
|
|
372
|
+
error: computed(() => error.value),
|
|
373
|
+
isReady,
|
|
374
|
+
connect,
|
|
375
|
+
callFunction
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
## Testing Patterns
|
|
381
|
+
|
|
382
|
+
### Unit Testing Pattern
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
// test/diamond-abi.test.ts
|
|
386
|
+
import { expect } from 'chai';
|
|
387
|
+
import { generateDiamondAbi, Diamond } from 'diamonds';
|
|
388
|
+
import { FileDeploymentRepository } from 'diamonds/repositories';
|
|
389
|
+
import { ethers } from 'hardhat';
|
|
390
|
+
|
|
391
|
+
describe('Diamond ABI Generation', () => {
|
|
392
|
+
let diamond: Diamond;
|
|
393
|
+
let repository: FileDeploymentRepository;
|
|
394
|
+
|
|
395
|
+
beforeEach(async () => {
|
|
396
|
+
const config = {
|
|
397
|
+
diamondName: 'TestDiamond',
|
|
398
|
+
networkName: 'localhost',
|
|
399
|
+
chainId: 31337,
|
|
400
|
+
deploymentsPath: './test/fixtures/diamonds',
|
|
401
|
+
contractsPath: './contracts'
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
repository = new FileDeploymentRepository(config);
|
|
405
|
+
diamond = new Diamond(config, repository);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
describe('Basic ABI Generation', () => {
|
|
409
|
+
it('should generate valid ABI', async () => {
|
|
410
|
+
const result = await generateDiamondAbi(diamond, {
|
|
411
|
+
validateSelectors: true,
|
|
412
|
+
verbose: false
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
expect(result.abi).to.be.an('array');
|
|
416
|
+
expect(result.abi.length).to.be.greaterThan(0);
|
|
417
|
+
expect(result.stats.totalFunctions).to.be.greaterThan(0);
|
|
418
|
+
expect(result.stats.duplicateSelectorsSkipped).to.equal(0);
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('should include metadata', async () => {
|
|
422
|
+
const result = await generateDiamondAbi(diamond, {
|
|
423
|
+
includeSourceInfo: true,
|
|
424
|
+
outputDir: './test/output'
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const artifact = JSON.parse(
|
|
428
|
+
require('fs').readFileSync(result.outputPath!, 'utf8')
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
expect(artifact._diamondMetadata).to.exist;
|
|
432
|
+
expect(artifact._diamondMetadata.diamondName).to.equal('TestDiamond');
|
|
433
|
+
expect(artifact._diamondMetadata.selectorMap).to.exist;
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
describe('Contract Integration', () => {
|
|
438
|
+
it('should work with ethers.js', async () => {
|
|
439
|
+
const result = await generateDiamondAbi(diamond);
|
|
440
|
+
|
|
441
|
+
// Mock deployment
|
|
442
|
+
const [deployer] = await ethers.getSigners();
|
|
443
|
+
const mockAddress = '0x1234567890123456789012345678901234567890';
|
|
444
|
+
|
|
445
|
+
// Create contract instance
|
|
446
|
+
const contract = new ethers.Contract(
|
|
447
|
+
mockAddress,
|
|
448
|
+
result.abi,
|
|
449
|
+
deployer
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
expect(contract.interface).to.exist;
|
|
453
|
+
expect(contract.interface.functions).to.exist;
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
describe('Error Handling', () => {
|
|
458
|
+
it('should handle missing facets gracefully', async () => {
|
|
459
|
+
// Test with diamond that has missing facet artifacts
|
|
460
|
+
const result = await generateDiamondAbi(diamond, {
|
|
461
|
+
verbose: true
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
expect(result.abi).to.be.an('array');
|
|
465
|
+
// Should still generate ABI with available facets
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
it('should detect selector collisions', async () => {
|
|
469
|
+
// Add duplicate selectors to registry
|
|
470
|
+
diamond.registerFunctionSelectors({
|
|
471
|
+
'0x12345678': {
|
|
472
|
+
facetName: 'TestFacet1',
|
|
473
|
+
priority: 100,
|
|
474
|
+
address: '0x1111111111111111111111111111111111111111',
|
|
475
|
+
action: 1
|
|
476
|
+
},
|
|
477
|
+
'0x12345678': {
|
|
478
|
+
facetName: 'TestFacet2',
|
|
479
|
+
priority: 100,
|
|
480
|
+
address: '0x2222222222222222222222222222222222222222',
|
|
481
|
+
action: 1
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const result = await generateDiamondAbi(diamond, {
|
|
486
|
+
validateSelectors: true,
|
|
487
|
+
verbose: true
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
expect(result.stats.duplicateSelectorsSkipped).to.be.greaterThan(0);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### Integration Testing Pattern
|
|
497
|
+
|
|
498
|
+
```typescript
|
|
499
|
+
// test/integration/diamond-abi-workflow.test.ts
|
|
500
|
+
import { expect } from 'chai';
|
|
501
|
+
import { spawn } from 'child_process';
|
|
502
|
+
import { existsSync, readFileSync } from 'fs';
|
|
503
|
+
import { join } from 'path';
|
|
504
|
+
|
|
505
|
+
describe('Diamond ABI Workflow Integration', () => {
|
|
506
|
+
const testOutputDir = './test/tmp/diamond-abi';
|
|
507
|
+
|
|
508
|
+
beforeEach(async () => {
|
|
509
|
+
// Clean up previous test outputs
|
|
510
|
+
if (existsSync(testOutputDir)) {
|
|
511
|
+
await import('fs').then(fs => fs.rmSync(testOutputDir, { recursive: true }));
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
it('should complete full CLI workflow', async () => {
|
|
516
|
+
// Test CLI command
|
|
517
|
+
const cliResult = await runCLI([
|
|
518
|
+
'generate',
|
|
519
|
+
'--diamond', 'TestDiamond',
|
|
520
|
+
'--network', 'localhost',
|
|
521
|
+
'--output', testOutputDir,
|
|
522
|
+
'--verbose'
|
|
523
|
+
]);
|
|
524
|
+
|
|
525
|
+
expect(cliResult.exitCode).to.equal(0);
|
|
526
|
+
expect(cliResult.stdout).to.include('ā
Diamond ABI generation completed');
|
|
527
|
+
|
|
528
|
+
// Verify output files
|
|
529
|
+
const artifactPath = join(testOutputDir, 'TestDiamond.json');
|
|
530
|
+
const typesPath = join(testOutputDir, 'TestDiamond.d.ts');
|
|
531
|
+
|
|
532
|
+
expect(existsSync(artifactPath)).to.be.true;
|
|
533
|
+
expect(existsSync(typesPath)).to.be.true;
|
|
534
|
+
|
|
535
|
+
// Verify artifact content
|
|
536
|
+
const artifact = JSON.parse(readFileSync(artifactPath, 'utf8'));
|
|
537
|
+
expect(artifact.contractName).to.equal('TestDiamond');
|
|
538
|
+
expect(artifact.abi).to.be.an('array');
|
|
539
|
+
expect(artifact._diamondMetadata).to.exist;
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('should handle TypeChain generation', async () => {
|
|
543
|
+
// Generate ABI first
|
|
544
|
+
await runCLI([
|
|
545
|
+
'generate',
|
|
546
|
+
'--diamond', 'TestDiamond',
|
|
547
|
+
'--output', testOutputDir
|
|
548
|
+
]);
|
|
549
|
+
|
|
550
|
+
// Generate TypeChain types
|
|
551
|
+
const typechainResult = await runCLI([
|
|
552
|
+
'npx', 'typechain',
|
|
553
|
+
'--target', 'ethers-v5',
|
|
554
|
+
'--out-dir', join(testOutputDir, 'typechain'),
|
|
555
|
+
join(testOutputDir, '*.json')
|
|
556
|
+
]);
|
|
557
|
+
|
|
558
|
+
expect(typechainResult.exitCode).to.equal(0);
|
|
559
|
+
expect(existsSync(join(testOutputDir, 'typechain'))).to.be.true;
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
async function runCLI(args: string[]): Promise<{
|
|
563
|
+
exitCode: number;
|
|
564
|
+
stdout: string;
|
|
565
|
+
stderr: string;
|
|
566
|
+
}> {
|
|
567
|
+
return new Promise((resolve) => {
|
|
568
|
+
const process = spawn('npm', ['run', 'diamond-abi', ...args], {
|
|
569
|
+
stdio: 'pipe'
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
let stdout = '';
|
|
573
|
+
let stderr = '';
|
|
574
|
+
|
|
575
|
+
process.stdout.on('data', (data) => {
|
|
576
|
+
stdout += data.toString();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
process.stderr.on('data', (data) => {
|
|
580
|
+
stderr += data.toString();
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
process.on('close', (code) => {
|
|
584
|
+
resolve({
|
|
585
|
+
exitCode: code || 0,
|
|
586
|
+
stdout,
|
|
587
|
+
stderr
|
|
588
|
+
});
|
|
589
|
+
});
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
## CI/CD Integration
|
|
596
|
+
|
|
597
|
+
### GitHub Actions Workflow
|
|
598
|
+
|
|
599
|
+
```yaml
|
|
600
|
+
# .github/workflows/diamond-abi.yml
|
|
601
|
+
name: Diamond ABI Generation and Validation
|
|
602
|
+
|
|
603
|
+
on:
|
|
604
|
+
push:
|
|
605
|
+
branches: [main, develop]
|
|
606
|
+
pull_request:
|
|
607
|
+
branches: [main]
|
|
608
|
+
|
|
609
|
+
jobs:
|
|
610
|
+
generate-abi:
|
|
611
|
+
runs-on: ubuntu-latest
|
|
612
|
+
|
|
613
|
+
strategy:
|
|
614
|
+
matrix:
|
|
615
|
+
network: [localhost, sepolia, mainnet]
|
|
616
|
+
diamond: [GameDiamond, TradingDiamond]
|
|
617
|
+
|
|
618
|
+
steps:
|
|
619
|
+
- name: Checkout code
|
|
620
|
+
uses: actions/checkout@v3
|
|
621
|
+
|
|
622
|
+
- name: Setup Node.js
|
|
623
|
+
uses: actions/setup-node@v3
|
|
624
|
+
with:
|
|
625
|
+
node-version: '18'
|
|
626
|
+
cache: 'npm'
|
|
627
|
+
|
|
628
|
+
- name: Install dependencies
|
|
629
|
+
run: npm ci
|
|
630
|
+
|
|
631
|
+
- name: Compile contracts
|
|
632
|
+
run: npm run compile
|
|
633
|
+
|
|
634
|
+
- name: Generate Diamond ABI
|
|
635
|
+
run: |
|
|
636
|
+
npm run diamond-abi generate \
|
|
637
|
+
--diamond ${{ matrix.diamond }} \
|
|
638
|
+
--network ${{ matrix.network }} \
|
|
639
|
+
--output ./artifacts/diamond-abi/${{ matrix.network }} \
|
|
640
|
+
--include-source \
|
|
641
|
+
--validate-selectors \
|
|
642
|
+
--verbose
|
|
643
|
+
|
|
644
|
+
- name: Generate TypeChain types
|
|
645
|
+
run: |
|
|
646
|
+
npx typechain \
|
|
647
|
+
--target ethers-v5 \
|
|
648
|
+
--out-dir ./typechain-types/${{ matrix.network }} \
|
|
649
|
+
./artifacts/diamond-abi/${{ matrix.network }}/*.json
|
|
650
|
+
|
|
651
|
+
- name: Validate ABI
|
|
652
|
+
run: |
|
|
653
|
+
npm run diamond-abi validate \
|
|
654
|
+
./artifacts/diamond-abi/${{ matrix.network }}/${{ matrix.diamond }}.json
|
|
655
|
+
|
|
656
|
+
- name: Upload ABI artifacts
|
|
657
|
+
uses: actions/upload-artifact@v3
|
|
658
|
+
with:
|
|
659
|
+
name: diamond-abi-${{ matrix.network }}-${{ matrix.diamond }}
|
|
660
|
+
path: |
|
|
661
|
+
./artifacts/diamond-abi/${{ matrix.network }}/
|
|
662
|
+
./typechain-types/${{ matrix.network }}/
|
|
663
|
+
|
|
664
|
+
validate-integration:
|
|
665
|
+
needs: generate-abi
|
|
666
|
+
runs-on: ubuntu-latest
|
|
667
|
+
|
|
668
|
+
steps:
|
|
669
|
+
- name: Checkout code
|
|
670
|
+
uses: actions/checkout@v3
|
|
671
|
+
|
|
672
|
+
- name: Setup Node.js
|
|
673
|
+
uses: actions/setup-node@v3
|
|
674
|
+
with:
|
|
675
|
+
node-version: '18'
|
|
676
|
+
cache: 'npm'
|
|
677
|
+
|
|
678
|
+
- name: Install dependencies
|
|
679
|
+
run: npm ci
|
|
680
|
+
|
|
681
|
+
- name: Download ABI artifacts
|
|
682
|
+
uses: actions/download-artifact@v3
|
|
683
|
+
with:
|
|
684
|
+
path: ./artifacts/
|
|
685
|
+
|
|
686
|
+
- name: Run integration tests
|
|
687
|
+
run: npm run test:integration
|
|
688
|
+
|
|
689
|
+
- name: Test TypeScript compilation
|
|
690
|
+
run: npx tsc --noEmit --project tsconfig.json
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### GitLab CI/CD Pipeline
|
|
694
|
+
|
|
695
|
+
```yaml
|
|
696
|
+
# .gitlab-ci.yml
|
|
697
|
+
stages:
|
|
698
|
+
- compile
|
|
699
|
+
- generate-abi
|
|
700
|
+
- validate
|
|
701
|
+
- deploy
|
|
702
|
+
|
|
703
|
+
variables:
|
|
704
|
+
NODE_VERSION: "18"
|
|
705
|
+
|
|
706
|
+
compile-contracts:
|
|
707
|
+
stage: compile
|
|
708
|
+
image: node:$NODE_VERSION
|
|
709
|
+
script:
|
|
710
|
+
- npm ci
|
|
711
|
+
- npm run compile
|
|
712
|
+
artifacts:
|
|
713
|
+
paths:
|
|
714
|
+
- artifacts/contracts/
|
|
715
|
+
- cache/
|
|
716
|
+
expire_in: 1 hour
|
|
717
|
+
|
|
718
|
+
generate-diamond-abi:
|
|
719
|
+
stage: generate-abi
|
|
720
|
+
image: node:$NODE_VERSION
|
|
721
|
+
needs: [compile-contracts]
|
|
722
|
+
parallel:
|
|
723
|
+
matrix:
|
|
724
|
+
- NETWORK: [localhost, sepolia, mainnet]
|
|
725
|
+
DIAMOND: [GameDiamond, TradingDiamond]
|
|
726
|
+
script:
|
|
727
|
+
- npm ci
|
|
728
|
+
- |
|
|
729
|
+
npm run diamond-abi generate \
|
|
730
|
+
--diamond $DIAMOND \
|
|
731
|
+
--network $NETWORK \
|
|
732
|
+
--output ./artifacts/diamond-abi/$NETWORK \
|
|
733
|
+
--include-source \
|
|
734
|
+
--validate-selectors \
|
|
735
|
+
--verbose
|
|
736
|
+
- |
|
|
737
|
+
npx typechain \
|
|
738
|
+
--target ethers-v5 \
|
|
739
|
+
--out-dir ./typechain-types/$NETWORK \
|
|
740
|
+
./artifacts/diamond-abi/$NETWORK/*.json
|
|
741
|
+
artifacts:
|
|
742
|
+
paths:
|
|
743
|
+
- artifacts/diamond-abi/
|
|
744
|
+
- typechain-types/
|
|
745
|
+
expire_in: 1 day
|
|
746
|
+
|
|
747
|
+
validate-abi:
|
|
748
|
+
stage: validate
|
|
749
|
+
image: node:$NODE_VERSION
|
|
750
|
+
needs: [generate-diamond-abi]
|
|
751
|
+
script:
|
|
752
|
+
- npm ci
|
|
753
|
+
- npm run test:integration
|
|
754
|
+
- npx tsc --noEmit
|
|
755
|
+
artifacts:
|
|
756
|
+
reports:
|
|
757
|
+
junit: test-results.xml
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
## Performance Optimization
|
|
761
|
+
|
|
762
|
+
### Caching Strategy
|
|
763
|
+
|
|
764
|
+
```typescript
|
|
765
|
+
// src/utils/diamond-abi-cache.ts
|
|
766
|
+
import { createHash } from 'crypto';
|
|
767
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
768
|
+
import { join } from 'path';
|
|
769
|
+
|
|
770
|
+
export class DiamondAbiCache {
|
|
771
|
+
private cacheDir: string;
|
|
772
|
+
|
|
773
|
+
constructor(cacheDir: string = './cache/diamond-abi') {
|
|
774
|
+
this.cacheDir = cacheDir;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
getCacheKey(diamond: Diamond, options: any): string {
|
|
778
|
+
const hashInput = JSON.stringify({
|
|
779
|
+
diamondName: diamond.diamondName,
|
|
780
|
+
networkName: diamond.networkName,
|
|
781
|
+
chainId: diamond.chainId,
|
|
782
|
+
deploymentHash: this.getDeploymentHash(diamond),
|
|
783
|
+
options
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
return createHash('sha256').update(hashInput).digest('hex');
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
async get(cacheKey: string): Promise<any | null> {
|
|
790
|
+
const cachePath = join(this.cacheDir, `${cacheKey}.json`);
|
|
791
|
+
|
|
792
|
+
if (!existsSync(cachePath)) {
|
|
793
|
+
return null;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
try {
|
|
797
|
+
const cached = JSON.parse(readFileSync(cachePath, 'utf8'));
|
|
798
|
+
|
|
799
|
+
// Check if cache is still valid (e.g., less than 1 hour old)
|
|
800
|
+
const maxAge = 60 * 60 * 1000; // 1 hour
|
|
801
|
+
if (Date.now() - cached.timestamp > maxAge) {
|
|
802
|
+
return null;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
return cached.data;
|
|
806
|
+
} catch (error) {
|
|
807
|
+
return null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
async set(cacheKey: string, data: any): Promise<void> {
|
|
812
|
+
const cachePath = join(this.cacheDir, `${cacheKey}.json`);
|
|
813
|
+
|
|
814
|
+
const cacheData = {
|
|
815
|
+
timestamp: Date.now(),
|
|
816
|
+
data
|
|
817
|
+
};
|
|
818
|
+
|
|
819
|
+
writeFileSync(cachePath, JSON.stringify(cacheData, null, 2));
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
private getDeploymentHash(diamond: Diamond): string {
|
|
823
|
+
const deploymentData = diamond.getDeployedDiamondData();
|
|
824
|
+
return createHash('sha256')
|
|
825
|
+
.update(JSON.stringify(deploymentData))
|
|
826
|
+
.digest('hex');
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Usage with caching
|
|
831
|
+
export async function generateDiamondAbiWithCache(
|
|
832
|
+
diamond: Diamond,
|
|
833
|
+
options: any = {}
|
|
834
|
+
): Promise<any> {
|
|
835
|
+
const cache = new DiamondAbiCache();
|
|
836
|
+
const cacheKey = cache.getCacheKey(diamond, options);
|
|
837
|
+
|
|
838
|
+
// Try to get from cache first
|
|
839
|
+
const cached = await cache.get(cacheKey);
|
|
840
|
+
if (cached) {
|
|
841
|
+
console.log('ā
Using cached Diamond ABI');
|
|
842
|
+
return cached;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Generate fresh ABI
|
|
846
|
+
const result = await generateDiamondAbi(diamond, options);
|
|
847
|
+
|
|
848
|
+
// Cache the result
|
|
849
|
+
await cache.set(cacheKey, result);
|
|
850
|
+
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Parallel Processing
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
// src/utils/parallel-abi-generator.ts
|
|
859
|
+
import { generateDiamondAbi } from 'diamonds';
|
|
860
|
+
import { Worker } from 'worker_threads';
|
|
861
|
+
import { cpus } from 'os';
|
|
862
|
+
|
|
863
|
+
export class ParallelAbiGenerator {
|
|
864
|
+
private maxWorkers: number;
|
|
865
|
+
|
|
866
|
+
constructor(maxWorkers: number = cpus().length) {
|
|
867
|
+
this.maxWorkers = maxWorkers;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
async generateMultiple(configs: DiamondConfig[]): Promise<any[]> {
|
|
871
|
+
const chunks = this.chunkArray(configs, this.maxWorkers);
|
|
872
|
+
const promises = chunks.map(chunk => this.processChunk(chunk));
|
|
873
|
+
const results = await Promise.all(promises);
|
|
874
|
+
|
|
875
|
+
return results.flat();
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
private chunkArray<T>(array: T[], chunkSize: number): T[][] {
|
|
879
|
+
const chunks: T[][] = [];
|
|
880
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
881
|
+
chunks.push(array.slice(i, i + chunkSize));
|
|
882
|
+
}
|
|
883
|
+
return chunks;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private async processChunk(configs: DiamondConfig[]): Promise<any[]> {
|
|
887
|
+
return new Promise((resolve, reject) => {
|
|
888
|
+
const worker = new Worker(`
|
|
889
|
+
const { parentPort } = require('worker_threads');
|
|
890
|
+
const { generateDiamondAbi, Diamond } = require('diamonds');
|
|
891
|
+
const { FileDeploymentRepository } = require('diamonds/repositories');
|
|
892
|
+
|
|
893
|
+
parentPort.on('message', async (configs) => {
|
|
894
|
+
const results = [];
|
|
895
|
+
|
|
896
|
+
for (const config of configs) {
|
|
897
|
+
try {
|
|
898
|
+
const repository = new FileDeploymentRepository(config);
|
|
899
|
+
const diamond = new Diamond(config, repository);
|
|
900
|
+
const result = await generateDiamondAbi(diamond);
|
|
901
|
+
results.push({ config, result, success: true });
|
|
902
|
+
} catch (error) {
|
|
903
|
+
results.push({ config, error: error.message, success: false });
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
parentPort.postMessage(results);
|
|
908
|
+
});
|
|
909
|
+
`, { eval: true });
|
|
910
|
+
|
|
911
|
+
worker.postMessage(configs);
|
|
912
|
+
|
|
913
|
+
worker.on('message', (results) => {
|
|
914
|
+
worker.terminate();
|
|
915
|
+
resolve(results);
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
worker.on('error', (error) => {
|
|
919
|
+
worker.terminate();
|
|
920
|
+
reject(error);
|
|
921
|
+
});
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
```
|
|
926
|
+
|
|
927
|
+
## Custom Tooling
|
|
928
|
+
|
|
929
|
+
### VS Code Extension Integration
|
|
930
|
+
|
|
931
|
+
```typescript
|
|
932
|
+
// src/vscode-extension/diamond-abi-provider.ts
|
|
933
|
+
import * as vscode from 'vscode';
|
|
934
|
+
import { generateDiamondAbi } from 'diamonds';
|
|
935
|
+
|
|
936
|
+
export class DiamondAbiProvider {
|
|
937
|
+
private context: vscode.ExtensionContext;
|
|
938
|
+
|
|
939
|
+
constructor(context: vscode.ExtensionContext) {
|
|
940
|
+
this.context = context;
|
|
941
|
+
this.registerCommands();
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
private registerCommands() {
|
|
945
|
+
const generateCommand = vscode.commands.registerCommand(
|
|
946
|
+
'diamond-abi.generate',
|
|
947
|
+
async () => {
|
|
948
|
+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
|
949
|
+
if (!workspaceFolder) {
|
|
950
|
+
vscode.window.showErrorMessage('No workspace folder found');
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const diamondName = await vscode.window.showInputBox({
|
|
955
|
+
prompt: 'Enter diamond name',
|
|
956
|
+
value: 'GameDiamond'
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
if (!diamondName) return;
|
|
960
|
+
|
|
961
|
+
const networkName = await vscode.window.showQuickPick(
|
|
962
|
+
['localhost', 'sepolia', 'mainnet'],
|
|
963
|
+
{ placeHolder: 'Select network' }
|
|
964
|
+
);
|
|
965
|
+
|
|
966
|
+
if (!networkName) return;
|
|
967
|
+
|
|
968
|
+
try {
|
|
969
|
+
await vscode.window.withProgress({
|
|
970
|
+
location: vscode.ProgressLocation.Notification,
|
|
971
|
+
title: 'Generating Diamond ABI...',
|
|
972
|
+
cancellable: false
|
|
973
|
+
}, async (progress) => {
|
|
974
|
+
progress.report({ increment: 0 });
|
|
975
|
+
|
|
976
|
+
// Generate ABI
|
|
977
|
+
const result = await generateDiamondAbi(diamond, {
|
|
978
|
+
outputDir: './artifacts/diamond-abi',
|
|
979
|
+
verbose: true
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
progress.report({ increment: 100 });
|
|
983
|
+
|
|
984
|
+
vscode.window.showInformationMessage(
|
|
985
|
+
`ā
Generated ABI with ${result.stats.totalFunctions} functions`
|
|
986
|
+
);
|
|
987
|
+
});
|
|
988
|
+
} catch (error) {
|
|
989
|
+
vscode.window.showErrorMessage(
|
|
990
|
+
`Failed to generate ABI: ${error}`
|
|
991
|
+
);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
);
|
|
995
|
+
|
|
996
|
+
this.context.subscriptions.push(generateCommand);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
### Custom CLI Tool
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
// scripts/advanced-diamond-cli.ts
|
|
1005
|
+
#!/usr/bin/env node
|
|
1006
|
+
|
|
1007
|
+
import { Command } from 'commander';
|
|
1008
|
+
import inquirer from 'inquirer';
|
|
1009
|
+
import chalk from 'chalk';
|
|
1010
|
+
import { generateDiamondAbi } from 'diamonds';
|
|
1011
|
+
|
|
1012
|
+
const program = new Command();
|
|
1013
|
+
|
|
1014
|
+
program
|
|
1015
|
+
.name('diamond-tools')
|
|
1016
|
+
.description('Advanced Diamond ABI tools')
|
|
1017
|
+
.version('1.0.0');
|
|
1018
|
+
|
|
1019
|
+
program
|
|
1020
|
+
.command('interactive')
|
|
1021
|
+
.description('Interactive ABI generation wizard')
|
|
1022
|
+
.action(async () => {
|
|
1023
|
+
console.log(chalk.blue('š® Diamond ABI Generation Wizard'));
|
|
1024
|
+
|
|
1025
|
+
const answers = await inquirer.prompt([
|
|
1026
|
+
{
|
|
1027
|
+
type: 'input',
|
|
1028
|
+
name: 'diamondName',
|
|
1029
|
+
message: 'Diamond name:',
|
|
1030
|
+
default: 'GameDiamond'
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
type: 'list',
|
|
1034
|
+
name: 'network',
|
|
1035
|
+
message: 'Select network:',
|
|
1036
|
+
choices: [
|
|
1037
|
+
{ name: 'Localhost (31337)', value: 'localhost' },
|
|
1038
|
+
{ name: 'Sepolia (11155111)', value: 'sepolia' },
|
|
1039
|
+
{ name: 'Mainnet (1)', value: 'mainnet' }
|
|
1040
|
+
]
|
|
1041
|
+
},
|
|
1042
|
+
{
|
|
1043
|
+
type: 'checkbox',
|
|
1044
|
+
name: 'features',
|
|
1045
|
+
message: 'Select features:',
|
|
1046
|
+
choices: [
|
|
1047
|
+
{ name: 'Include source information', value: 'includeSource' },
|
|
1048
|
+
{ name: 'Validate selectors', value: 'validateSelectors' },
|
|
1049
|
+
{ name: 'Generate TypeChain types', value: 'generateTypes' },
|
|
1050
|
+
{ name: 'Generate Viem types', value: 'generateViem' },
|
|
1051
|
+
{ name: 'Verbose output', value: 'verbose' }
|
|
1052
|
+
]
|
|
1053
|
+
}
|
|
1054
|
+
]);
|
|
1055
|
+
|
|
1056
|
+
try {
|
|
1057
|
+
const options = {
|
|
1058
|
+
outputDir: './artifacts/diamond-abi',
|
|
1059
|
+
includeSourceInfo: answers.features.includes('includeSource'),
|
|
1060
|
+
validateSelectors: answers.features.includes('validateSelectors'),
|
|
1061
|
+
verbose: answers.features.includes('verbose')
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
const result = await generateDiamondAbi(diamond, options);
|
|
1065
|
+
|
|
1066
|
+
console.log(chalk.green('\nā
ABI generation completed!'));
|
|
1067
|
+
console.log(chalk.cyan(`Functions: ${result.stats.totalFunctions}`));
|
|
1068
|
+
console.log(chalk.cyan(`Events: ${result.stats.totalEvents}`));
|
|
1069
|
+
console.log(chalk.cyan(`Facets: ${result.stats.facetCount}`));
|
|
1070
|
+
|
|
1071
|
+
if (answers.features.includes('generateTypes')) {
|
|
1072
|
+
console.log(chalk.blue('\nš Generating TypeChain types...'));
|
|
1073
|
+
// Generate TypeChain types
|
|
1074
|
+
const { exec } = require('child_process');
|
|
1075
|
+
exec('npx typechain --target ethers-v5 --out-dir typechain-types artifacts/diamond-abi/*.json');
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
if (answers.features.includes('generateViem')) {
|
|
1079
|
+
console.log(chalk.blue('\nš Generating Viem types...'));
|
|
1080
|
+
// Generate Viem types
|
|
1081
|
+
// Implementation here
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
console.error(chalk.red('ā Error:'), error);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
program.parse();
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
## Migration Strategies
|
|
1093
|
+
|
|
1094
|
+
### From Hardhat to Diamond ABI
|
|
1095
|
+
|
|
1096
|
+
```typescript
|
|
1097
|
+
// scripts/migrate-to-diamond-abi.ts
|
|
1098
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
1099
|
+
import { join } from 'path';
|
|
1100
|
+
import { glob } from 'glob';
|
|
1101
|
+
|
|
1102
|
+
export class MigrationTool {
|
|
1103
|
+
async migrateFromHardhat() {
|
|
1104
|
+
console.log('š Migrating from Hardhat artifacts to Diamond ABI...');
|
|
1105
|
+
|
|
1106
|
+
// Find all existing contract artifacts
|
|
1107
|
+
const artifactPaths = glob.sync('./artifacts/contracts/**/*.json');
|
|
1108
|
+
|
|
1109
|
+
for (const artifactPath of artifactPaths) {
|
|
1110
|
+
if (artifactPath.includes('.dbg.json')) continue;
|
|
1111
|
+
|
|
1112
|
+
const artifact = JSON.parse(readFileSync(artifactPath, 'utf8'));
|
|
1113
|
+
|
|
1114
|
+
// Check if this is a diamond-related contract
|
|
1115
|
+
if (this.isDiamondContract(artifact)) {
|
|
1116
|
+
await this.convertArtifact(artifact, artifactPath);
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
console.log('ā
Migration completed');
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
private isDiamondContract(artifact: any): boolean {
|
|
1124
|
+
// Check for diamond-specific patterns
|
|
1125
|
+
const diamondPatterns = [
|
|
1126
|
+
'Diamond',
|
|
1127
|
+
'Facet',
|
|
1128
|
+
'Loupe',
|
|
1129
|
+
'Cut'
|
|
1130
|
+
];
|
|
1131
|
+
|
|
1132
|
+
return diamondPatterns.some(pattern =>
|
|
1133
|
+
artifact.contractName.includes(pattern)
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
private async convertArtifact(artifact: any, originalPath: string) {
|
|
1138
|
+
// Convert to diamond ABI format
|
|
1139
|
+
const diamondArtifact = {
|
|
1140
|
+
...artifact,
|
|
1141
|
+
_diamondMetadata: {
|
|
1142
|
+
generatedAt: new Date().toISOString(),
|
|
1143
|
+
migratedFrom: originalPath,
|
|
1144
|
+
originalFormat: 'hardhat-artifact'
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// Write to new location
|
|
1149
|
+
const newPath = originalPath.replace('/artifacts/contracts/', '/artifacts/diamond-abi/');
|
|
1150
|
+
writeFileSync(newPath, JSON.stringify(diamondArtifact, null, 2));
|
|
1151
|
+
|
|
1152
|
+
console.log(`ā
Converted ${artifact.contractName}`);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
```
|
|
1156
|
+
|
|
1157
|
+
### Version Upgrade Strategy
|
|
1158
|
+
|
|
1159
|
+
```typescript
|
|
1160
|
+
// scripts/upgrade-diamond-abi.ts
|
|
1161
|
+
export class UpgradeManager {
|
|
1162
|
+
async upgradeToLatestVersion() {
|
|
1163
|
+
console.log('š Upgrading Diamond ABI to latest version...');
|
|
1164
|
+
|
|
1165
|
+
// Backup existing ABIs
|
|
1166
|
+
await this.backupExistingAbis();
|
|
1167
|
+
|
|
1168
|
+
// Regenerate with latest features
|
|
1169
|
+
await this.regenerateAbis();
|
|
1170
|
+
|
|
1171
|
+
// Validate upgrade
|
|
1172
|
+
await this.validateUpgrade();
|
|
1173
|
+
|
|
1174
|
+
console.log('ā
Upgrade completed');
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
private async backupExistingAbis() {
|
|
1178
|
+
const backupDir = `./artifacts/diamond-abi-backup-${Date.now()}`;
|
|
1179
|
+
// Copy existing ABIs to backup directory
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
private async regenerateAbis() {
|
|
1183
|
+
// Regenerate all ABIs with latest version
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
private async validateUpgrade() {
|
|
1187
|
+
// Run tests to ensure upgrade was successful
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
## Conclusion
|
|
1193
|
+
|
|
1194
|
+
These practical examples and patterns provide a comprehensive foundation for implementing the Diamond ABI Generator in real-world projects. The patterns cover:
|
|
1195
|
+
|
|
1196
|
+
- **Integration Patterns**: Basic setup, multi-network support, and development workflows
|
|
1197
|
+
- **Frontend Integration**: React, Next.js, and Vue.js examples
|
|
1198
|
+
- **Testing**: Unit and integration testing strategies
|
|
1199
|
+
- **CI/CD**: Automated workflows for different platforms
|
|
1200
|
+
- **Performance**: Caching and parallel processing optimizations
|
|
1201
|
+
- **Custom Tooling**: VS Code extensions and CLI tools
|
|
1202
|
+
- **Migration**: Strategies for adopting the Diamond ABI system
|
|
1203
|
+
|
|
1204
|
+
Choose the patterns that best fit your project's needs and customize them according to your specific requirements.
|