@patchworkdev/pdk
Version:
Patchwork Development Kit
132 lines (131 loc) • 7.18 kB
JavaScript
;
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;