UNPKG

@patchworkdev/pdk

Version:

Patchwork Development Kit

132 lines (131 loc) 7.18 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContractProcessor = void 0; const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const block_number_1 = require("../services/block-number"); class ContractProcessor { blockNumberService; constructor() { this.blockNumberService = new block_number_1.BlockNumberService(); } async processContracts(configPath, config = {}, shouldDeploy = false) { const action = shouldDeploy ? 'Deploying' : 'Calculating addresses and bytecode for'; console.info(`${action} contracts...`); const targetDir = path_1.default.dirname(configPath); const contractsDir = path_1.default.join(targetDir, 'contracts'); const scriptDir = path_1.default.join(contractsDir, 'script'); const deployConfig = { rpcUrl: shouldDeploy ? config.rpcUrl || 'http://localhost:1234' : undefined, privateKey: config.privateKey || '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80', owner: config.owner || '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', patchworkProtocol: config.patchworkProtocol || '0x00000000001616E65bb9FdA42dFBb7155406549b', }; if (shouldDeploy && !deployConfig.rpcUrl) { throw new Error('Missing required RPC URL for deployment'); } try { const deployScript = await this.findDeployScript(scriptDir); const scriptPath = path_1.default.join(scriptDir, deployScript); const contractNames = await this.extractContractNamesFromScript(scriptPath); console.info(`\nFound contracts: ${contractNames.join(', ')}`); const forgeArgs = await this.buildForgeArgs(deployScript, shouldDeploy, deployConfig); const { stdout } = await this.runForgeCommand(forgeArgs, scriptDir, deployConfig); // Get deployment block if deploying const deploymentBlock = shouldDeploy && deployConfig.rpcUrl ? await this.blockNumberService.getDeploymentBlockNumber(deployConfig.rpcUrl) : 0n; const deployedContracts = await this.parseDeploymentOutput(stdout, contractNames, Number(deploymentBlock)); this.displayResults(deployedContracts); return deployedContracts; } catch (error) { console.error(`${shouldDeploy ? 'Deployment' : 'Calculation'} failed:`, error); throw error; } } async findDeployScript(scriptDir) { const files = await promises_1.default.readdir(scriptDir); const deployScripts = files.filter((file) => file.endsWith('-deploy.s.sol')); if (deployScripts.length === 0) { throw new Error(`No deploy script found in ${scriptDir}`); } if (deployScripts.length > 1) { throw new Error(`Multiple deploy scripts found in ${scriptDir}: ${deployScripts.join(', ')}`); } return deployScripts[0]; } async extractContractNamesFromScript(scriptPath) { const content = await promises_1.default.readFile(scriptPath, 'utf-8'); const structMatch = content.match(/struct\s+DeploymentAddresses\s*{([^}]+)}/s); if (!structMatch) { throw new Error('Could not find DeploymentAddresses struct in script'); } return structMatch[1] .split('\n') .map((line) => line.trim()) .filter((line) => line.startsWith('DeploymentInfo')) .map((line) => line.split(/\s+/)[1].replace(';', '')); } async buildForgeArgs(deployScript, shouldDeploy, deployConfig) { const forgeArgs = ['script', '--optimize', '--optimizer-runs=200', '-vvv', deployScript]; if (shouldDeploy && deployConfig.rpcUrl) { forgeArgs.push('--rpc-url', deployConfig.rpcUrl, '--private-key', deployConfig.privateKey, '--broadcast'); } else { forgeArgs.push('--offline'); } return forgeArgs; } async runForgeCommand(forgeArgs, scriptDir, deployConfig) { const { execa } = await import('execa'); return execa('forge', forgeArgs, { cwd: scriptDir, env: { ...process.env, OWNER: deployConfig.owner, PATCHWORK_PROTOCOL: deployConfig.patchworkProtocol, TRY_DEPLOY: deployConfig.rpcUrl ? 'true' : 'false', }, stdio: ['inherit', 'pipe', 'inherit'], }); } async parseDeploymentOutput(output, contractNames, deploymentBlock) { const deployedContracts = {}; const lines = output.split('\n'); const returnLine = lines.find((line) => line.includes('DeploymentAddresses({')); if (!returnLine) { console.error('Deployment output:', output); throw new Error('Could not find deployment addresses in output'); } for (const contractName of contractNames) { const regex = new RegExp(`${contractName}:\\s*DeploymentInfo\\({\\s*deployedAddress:\\s*(0x[a-fA-F0-9]{40}),\\s*bytecodeHash:\\s*(0x[a-fA-F0-9]{64})\\s*}`); const match = returnLine.match(regex); if (match) { deployedContracts[contractName] = { deployedAddress: match[1], bytecodeHash: match[2], deploymentBlock, }; } } const missingContracts = contractNames.filter((name) => !deployedContracts[name]); if (missingContracts.length > 0) { console.error('Deployment output:', output); throw new Error(`Missing addresses for contracts: ${missingContracts.join(', ')}`); } return deployedContracts; } displayResults(deployedContracts) { console.info('\nResults:'); console.info('═════════════════════════════════════════════════════════════════════════════════════════'); console.info('Contract Name'.padEnd(20), '│', 'Address'.padEnd(42), '│', 'Block'.padEnd(10), '│', 'Bytecode Hash'); console.info('─'.repeat(20), '┼', '─'.repeat(42), '┼', '─'.repeat(10), '┼', '─'.repeat(66)); Object.entries(deployedContracts).forEach(([contract, info]) => { console.info(contract.padEnd(20), '│', info.deployedAddress.padEnd(42), '│', info.deploymentBlock.toString().padEnd(10), '│', info.bytecodeHash); }); console.info('═════════════════════════════════════════════════════════════════════════════════════════'); } } exports.ContractProcessor = ContractProcessor;