UNPKG

@m3s/smart-contract

Version:

A modular toolkit for generating, compiling, deploying, and interacting with Ethereum-compatible smart contracts

210 lines 9.66 kB
import { JsonRpcProvider } from "ethers"; // Ensure all needed types are imported import * as fs from "fs/promises"; import * as path from "path"; import SolidityCompiler from "./compilers/solidityCompiler.js"; import CodeGenerator from "./generator.js"; import { AdapterError, SmartContractErrorCode } from "@m3s/common"; export class OpenZeppelinAdapter { name; version; initialized = false; workDir; preserveOutput; providerConfig; defaultProvider; generator; solidityCompiler; solidityCompilerConfig; constructor(args) { const defaultWorkDir = path.join(process.cwd(), 'contracts'); this.workDir = args.options?.workDir || defaultWorkDir; this.preserveOutput = args.options?.preserveOutput ?? false; this.providerConfig = args.options?.providerConfig; this.name = args.name; this.version = args.version; // Configuration specific to helpers this.solidityCompilerConfig = { workDir: this.workDir, // Pass the determined workDir solcVersion: args.options?.solcVersion || '0.8.22', compilerSettings: args.options?.compilerSettings || { optimizer: { enabled: true, runs: 200 } }, hardhatConfigFileName: args.options?.hardhatConfig?.configFileName || 'hardhat.config.cjs', preserveOutput: this.preserveOutput, }; // Create helper instances this.generator = new CodeGenerator(); this.solidityCompiler = new SolidityCompiler(this.solidityCompilerConfig); } static async create(args) { const adapter = new OpenZeppelinAdapter(args); await adapter.initialize(); return adapter; } async initialize() { if (this.initialized) return; console.log(`[OpenZeppelinAdapter] Initializing...`); try { // 1. Ensure work directory exists await fs.mkdir(this.workDir, { recursive: true }); console.log(`[OpenZeppelinAdapter] Work directory ensured: ${this.workDir}`); // 2. Initialize OpenZeppelinAdapter's optional default EVM provider let adapterRpcUrl; if (this.providerConfig) { if (this.providerConfig.rpcUrls && Array.isArray(this.providerConfig.rpcUrls) && this.providerConfig.rpcUrls.length > 0) { adapterRpcUrl = this.providerConfig.rpcUrls[0]; console.log(`[OpenZeppelinAdapter] Using rpcUrls[0] for its default provider: ${adapterRpcUrl}`); } } if (adapterRpcUrl) { try { console.log(`[OpenZeppelinAdapter] Configuring its default EVM provider from: ${adapterRpcUrl}`); const chainId = this.providerConfig?.chainId ? Number(this.providerConfig.chainId) : undefined; this.defaultProvider = new JsonRpcProvider(adapterRpcUrl, chainId); await this.defaultProvider.getNetwork(); // Test connection console.log(`[OpenZeppelinAdapter] Its default EVM provider connected successfully to network: ${(await this.defaultProvider.getNetwork()).name}`); } catch (providerError) { console.warn(`[OpenZeppelinAdapter] Failed to initialize its default EVM provider: ${providerError.message}`); this.defaultProvider = undefined; } } else { console.log(`[OpenZeppelinAdapter] No suitable RPC URL found in providerConfig for its own default provider.`); } this.initialized = true; console.log(`[OpenZeppelinAdapter] Initialized successfully.`); } catch (error) { this.initialized = false; console.error(`[OpenZeppelinAdapter] Initialization failed: ${error.message}`, error.stack); if (error instanceof AdapterError) throw error; throw new AdapterError(`Failed to initialize OpenZeppelinAdapter: ${error.message}`, { cause: error, code: SmartContractErrorCode.AdapterNotInitialized, methodName: 'initialize' }); } } isInitialized() { return this.initialized; } // // --- Delegate to Internal Helpers --- // async generateContract(input: GenerateContractInput): Promise<string> { // if (!this.initialized) { // throw new AdapterError("Adapter not initialized", { // code: SmartContractErrorCode.AdapterNotInitialized, // methodName: 'generateContract' // }); // } // try { // return await this.generator.generate(input); // } catch (error: any) { // if (error instanceof AdapterError) throw error; // throw new AdapterError(`Contract generation failed: ${error.message}`, { // cause: error, // code: SmartContractErrorCode.MethodCallFailed, // Or a more specific SC_GENERATION_FAILED if added // methodName: 'generateContract', // details: { input } // }); // } // } async generateContract(input) { if (!this.initialized) { throw new AdapterError("Adapter not initialized", { code: SmartContractErrorCode.AdapterNotInitialized, methodName: 'generateContract' }); } // ✅ FIX: Template-specific validation const { template, options } = input; if (template?.includes('erc20') || template?.includes('erc721')) { // ERC20 and ERC721 require both name and symbol if (!options?.name || !options?.symbol) { throw new AdapterError("Contract 'name' and 'symbol' are required for ERC20/ERC721 templates", { code: SmartContractErrorCode.InvalidInput, methodName: 'generateContract', details: { template, providedOptions: options } }); } } else if (template?.includes('erc1155')) { // ERC1155 requires name and uri, but NOT symbol if (!options?.name || !options?.uri) { throw new AdapterError("Contract 'name' and 'uri' are required for ERC1155 templates", { code: SmartContractErrorCode.InvalidInput, methodName: 'generateContract', details: { template, providedOptions: options } }); } } try { const code = await this.generator.generate(input); console.log('GENERATED CODE --- generateContract', code); return code; } catch (error) { if (error instanceof AdapterError) throw error; throw new AdapterError(`Contract generation failed: ${error.message}`, { cause: error, code: SmartContractErrorCode.MethodCallFailed, methodName: 'generateContract', details: { input } }); } } async compile(input) { const methodName = 'compile'; if (!this.initialized) { throw new AdapterError("Adapter not initialized", { code: SmartContractErrorCode.AdapterNotInitialized, methodName }); } console.log(`[OpenZeppelinAdapter] Routing compile request for language: ${input.language}`); try { // --- Language-Based Compiler Routing --- switch (input.language.toLowerCase()) { case 'solidity': console.log(`[OpenZeppelinAdapter] Using SolidityCompiler (solc ${this.solidityCompilerConfig.solcVersion})...`); return await this.solidityCompiler.compile(input); case 'cairo': throw new AdapterError(`Compilation for 'cairo' is not yet implemented.`, { code: SmartContractErrorCode.InvalidInput, methodName, details: { language: input.language } }); case 'stellar': throw new AdapterError(`Compilation for 'stellar' is not yet implemented.`, { code: SmartContractErrorCode.InvalidInput, methodName, details: { language: input.language } }); case 'stylus': throw new AdapterError(`Compilation for 'stylus' is not yet implemented.`, { code: SmartContractErrorCode.InvalidInput, methodName, details: { language: input.language } }); default: throw new AdapterError(`Compilation not supported for language: ${input.language}`, { code: SmartContractErrorCode.InvalidInput, methodName, details: { language: input.language } }); } } catch (error) { if (error instanceof AdapterError) throw error; throw new AdapterError(`Compilation failed: ${error.message}`, { cause: error, code: SmartContractErrorCode.CompilationFailed, methodName, details: { input } }); } } } //# sourceMappingURL=adapter.js.map