@m3s/smart-contract
Version:
A modular toolkit for generating, compiling, deploying, and interacting with Ethereum-compatible smart contracts
191 lines • 11.8 kB
JavaScript
// import { CompileInput, CompiledOutput } from '../../../types/index.js'; // Adjust path
// import { promisify } from "util";
// import { exec } from "child_process";
// import * as fs from "fs/promises";
// import * as path from "path";
export {};
// export default class CairoCompiler {
// private workDir: string;
// private compilerSettings: any; // Now holds potential Cairo-specific settings
// private preserveOutput: boolean;
// private execAsync = promisify(exec);
// constructor(config: {
// workDir: string;
// compilerSettings?: {
// cairo?: { // Namespace Cairo settings
// openzeppelinTag?: string; // e.g., "v0.14.0"
// starknetVersion?: string; // e.g., ">=2.6.3"
// edition?: string; // e.g., "2023_11"
// // Add other scarb/cairo settings if needed
// };
// // Keep existing general/solidity settings if any
// [key: string]: any;
// };
// preserveOutput: boolean;
// }) {
// this.workDir = config.workDir;
// this.compilerSettings = config.compilerSettings || {};
// this.preserveOutput = config.preserveOutput;
// // Use a more recent default tag if none is provided in settings
// const defaultOzTag = 'v1.0.0'; // <<< CHANGE THIS LINE
// console.log(`[CairoCompiler] Initialized (using Scarb). OZ Tag default/override: ${this.compilerSettings?.cairo?.openzeppelinTag || defaultOzTag}`);
// }
// async compile(input: CompileInput): Promise<CompiledOutput> {
// const { sourceCode, language, contractName: inputContractName } = input;
// console.log(`[CairoCompiler] Attempting to compile Cairo contract...`);
// if (language.toLowerCase() !== 'cairo') {
// throw new Error(`[CairoCompiler] This compiler only supports 'cairo'. Language provided: ${language}`);
// }
// // --- Check Scarb Availability Early & Capture Version ---
// let scarbVersion = 'unknown'; // Declare scarbVersion in the outer scope
// try {
// const { stdout: versionStdout } = await this.execAsync('scarb --version'); // Use unique name for stdout
// scarbVersion = versionStdout.trim();
// console.log(`[CairoCompiler] Found Scarb: ${scarbVersion}`);
// } catch (versionError) {
// console.error("[CairoCompiler] Error: 'scarb' command not found or failed. Ensure Scarb (Starknet toolchain) is installed and in PATH.");
// console.error("[CairoCompiler] See Starknet documentation for installation: https://docs.starknet.io/develop/getting-started/installation");
// throw new Error("'scarb' not found. Cairo compilation requires the Scarb toolchain.");
// }
// // --- Determine Contract & Project Names ---
// const contractName = inputContractName || sourceCode.match(/mod\s+([a-zA-Z0-9_]+)/)?.[1];
// if (!contractName) throw new Error("Could not determine contract name from source or input");
// // --- Define Paths ---
// const hashInput = contractName + sourceCode + JSON.stringify(this.compilerSettings?.cairo || {});
// const hash = Buffer.from(hashInput).toString('base64').replace(/[/+=]/g, '').substring(0, 8);
// const contractDir = path.join(this.workDir, `cairo_${contractName}_${hash}`);
// const scarbPackageName = `m3s_scarb_project_${hash}`.toLowerCase();
// const sierraArtifactPath = path.join(contractDir, 'target', 'dev', `${scarbPackageName}_${contractName}.contract_class.json`);
// const casmArtifactPath = path.join(contractDir, 'target', 'dev', `${scarbPackageName}_${contractName}.compiled_contract_class.json`);
// const sourceFilePath = path.join(contractDir, 'src', 'lib.cairo');
// // --- Check Cache ---
// if (this.preserveOutput) {
// try {
// const existingSierraJson = await fs.readFile(sierraArtifactPath, 'utf-8');
// const existingSierraArtifact = JSON.parse(existingSierraJson);
// if (existingSierraArtifact && existingSierraArtifact.abi && existingSierraArtifact.sierra_program) {
// console.log(`[CairoCompiler] Using cached Scarb artifact for ${contractName} from ${contractDir}`);
// let casmArtifact = undefined;
// try {
// const existingCasmJson = await fs.readFile(casmArtifactPath, 'utf-8');
// casmArtifact = JSON.parse(existingCasmJson);
// } catch { /* CASM might not exist */ }
// return { // <<< RETURN CACHED RESULT
// artifacts: {
// abi: existingSierraArtifact.abi,
// sierra: existingSierraArtifact,
// casm: casmArtifact,
// contractName: contractName,
// sourceName: 'lib.cairo',
// },
// metadata: {
// compiler: 'scarb',
// compilerVersion: scarbVersion.split(' ')[1] || scarbVersion, // Use captured version
// language: 'cairo',
// contractName: contractName,
// }
// };
// }
// } catch (readError) { /* Cache miss, continue */ }
// }
// // --- Compilation Required ---
// try {
// // --- Prepare Scarb Project ---
// console.log(`[CairoCompiler] Preparing Scarb project for ${contractName} in temporary directory: ${contractDir}`);
// await fs.rm(contractDir, { recursive: true, force: true });
// await fs.mkdir(path.join(contractDir, 'src'), { recursive: true });
// await fs.writeFile(sourceFilePath, sourceCode);
// // Generate Scarb.toml content
// const scarbTomlPath = path.join(contractDir, 'Scarb.toml');
// const defaultOzTag = 'v1.0.0';
// const ozTag = this.compilerSettings?.cairo?.openzeppelinTag || defaultOzTag;
// const defaultStarknetVersion = ">=2.6.3";
// const starknetVersion = this.compilerSettings?.cairo?.starknetVersion || defaultStarknetVersion;
// const defaultCairoEdition = "2023_11";
// const cairoEdition = this.compilerSettings?.cairo?.edition || defaultCairoEdition;
// const scarbTomlContent = `
// [package]
// name = "${scarbPackageName}"
// version = "0.1.0"
// edition = "${cairoEdition}"
// [dependencies]
// starknet = "${starknetVersion}"
// openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", tag = "${ozTag}" }
// [[target.starknet-contract]]
// sierra = true
// casm = true
// `;
// await fs.writeFile(scarbTomlPath, scarbTomlContent.trim());
// console.log(`[CairoCompiler] Generated Scarb.toml with OZ tag: ${ozTag}`);
// // --- Execute Scarb Build ---
// const compileCommand = `cd "${contractDir}" && scarb build`;
// console.log(`[CairoCompiler] Executing: ${compileCommand}`);
// // Use unique names for build stdout/stderr
// const { stdout: buildStdout, stderr: buildStderr } = await this.execAsync(compileCommand);
// if (buildStdout) console.log(`[CairoCompiler] Scarb stdout:\n${buildStdout}`);
// if (buildStderr) console.warn(`[CairoCompiler] Scarb stderr:\n${buildStderr}`);
// // --- Read Artifacts ---
// console.log(`[CairoCompiler] Reading Sierra artifact (Contract Class): ${sierraArtifactPath}`);
// const sierraJson = await fs.readFile(sierraArtifactPath, 'utf-8');
// const sierraArtifact = JSON.parse(sierraJson);
// let casmArtifact = undefined;
// try {
// console.log(`[CairoCompiler] Reading CASM artifact (Compiled Class): ${casmArtifactPath}`);
// const casmJson = await fs.readFile(casmArtifactPath, 'utf-8');
// casmArtifact = JSON.parse(casmJson);
// } catch {
// console.warn(`[CairoCompiler] CASM artifact not found or failed to read at ${casmArtifactPath}.`);
// }
// // Basic validation
// if (!sierraArtifact || !sierraArtifact.abi || !sierraArtifact.sierra_program) {
// throw new Error("Cairo compilation using Scarb succeeded but artifact missing Sierra program or ABI.");
// }
// // --- Format Output ---
// const compiledOutput: CompiledOutput = {
// artifacts: {
// abi: sierraArtifact.abi,
// sierra: sierraArtifact,
// casm: casmArtifact,
// contractName: contractName,
// sourceName: 'lib.cairo',
// },
// metadata: {
// compiler: 'scarb',
// compilerVersion: scarbVersion.split(' ')[1] || scarbVersion, // Use captured version
// language: 'cairo',
// contractName: contractName,
// }
// };
// // --- Write README if preserving output ---
// if (this.preserveOutput) {
// try {
// const readmePath = path.join(contractDir, 'README.md');
// const readmeContent = `# ${contractName} (Cairo / Scarb)\n\nCompiled by M3S CairoCompiler on ${new Date().toISOString()}\nCompiler: ${scarbVersion}\nOZ Contracts Tag: ${ozTag}\n\nSource: \`src/lib.cairo\`\nSierra (Contract Class): \`target/dev/${path.basename(sierraArtifactPath)}\`\nCASM (Compiled Class): \`${casmArtifact ? `target/dev/${path.basename(casmArtifactPath)}` : 'N/A'}\``;
// await fs.writeFile(readmePath, readmeContent);
// } catch (err: any) { console.warn(`[CairoCompiler] Failed to write README: ${err.message}`); }
// }
// console.log(`[CairoCompiler] Scarb compilation successful for ${contractName}.`);
// return compiledOutput;
// } catch (error: any) {
// console.error(`[CairoCompiler] Scarb compilation failed: ${error.message}`, error.stack);
// // Check if stderr was captured during build execution (it might be on the error object if execAsync throws)
// if (error.stderr) console.error("Compilation stderr:", error.stderr);
// if (error.message.includes('command failed')) {
// throw new Error(`Scarb build command failed. Check Scarb output/stderr for details. Ensure source code is valid and dependencies are correct. Error: ${error.message}`);
// }
// throw new Error(`Cairo (Scarb) compilation failed: ${error.message}`);
// } finally {
// // --- Cleanup ---
// // Use the contractDir defined outside the try block
// if (contractDir && !this.preserveOutput) {
// try {
// await fs.rm(contractDir, { recursive: true, force: true });
// console.log(`[CairoCompiler] Cleaned up temporary Scarb project directory: ${contractDir}`);
// } catch (cleanupError: any) {
// console.warn(`[CairoCompiler] Failed to cleanup temporary Scarb project directory ${contractDir}: ${cleanupError.message}`);
// }
// }
// }
// }
// }
//# sourceMappingURL=cairoCompiler.js.map