import crypto from "crypto"; import fs from "fs"; import path from "path"; import { CheckCodeEvidenceEntry, updateCheckCodeEvidenceIndex, } from "./checkCodeReport"; import type { DeploymentInfo } from "./deploymentInfo"; export type ManifestSyncStatus = | "not_required" | "already_current" | "synced" | "proxy_kind_unknown" | "failed"; export type CheckCodeStatus = | "compile_failed" | "check_failed" | "sync_partial" | "succeeded"; export type EffectiveContractInfo = Record>; export interface CheckCodeOptions { root: string; runId: string; artifactHash: string; networks?: string[]; contracts?: string[]; deploymentAudit?: { deploymentId: string; registryCommitId: string; }; } export interface CheckCodeDeploymentAuditEvidence { deploymentId: string; registryCommitId: string; registryCommitPath: string; registryCommitHash: string; selections: Array<{ network: string; executionId: string }>; } export interface CommandResult { status: number; error?: string; } export interface CommandEvidence { command: string; args: string[]; status: number; network?: string; manifestDir?: string; manifestFile?: string; error?: string; } export interface CheckCodeEntry { network: string; instanceId: string; contractInfoKey: string; actualContract: string; address: string; proxyKind?: string; implementationAddress?: string; manifestDir: string; manifestFile: string; syncStatus: ManifestSyncStatus; beforeHash: string; afterHash: string; } export interface CheckCodeRunReport { version: 1; runId: string; status: CheckCodeStatus; artifactHash: string; sourceContractInfoPath: "contractInfo.json"; deploymentAudit?: CheckCodeDeploymentAuditEvidence; effectiveContractInfoPath?: string; rawCheckReportPath?: string; entries: CheckCodeEntry[]; commands: CommandEvidence[]; startedAt: string; completedAt: string; error?: string; reportPath: string; } export interface CheckCodeDependencies { runCommand: ( command: string, args: string[], options?: { cwd: string; env?: NodeJS.ProcessEnv }, ) => Promise | CommandResult; loadContractInfo: () => EffectiveContractInfo; loadDeploymentInfo: () => DeploymentInfo; resolveManifestDir: (network: string) => string; resolveManifestFile: ( network: string, manifestDir: string, ) => Promise | string; getImplementation: ( network: string, address: string, ) => Promise; getStorageLayoutHash: ( contractName: string, ) => Promise; now?: () => string; } const SAFE_MANIFEST_DIR = /^\.openzeppelin(?:\.[a-z0-9][a-z0-9-]*)?$/; const SAFE_MANIFEST_FILE = /^\.openzeppelin(?:\.[a-z0-9][a-z0-9-]*)?\/[A-Za-z0-9][A-Za-z0-9._-]*\.json$/; const HASH = /^[0-9a-f]{64}$/; const isRecord = (value: unknown): value is Record => ( Boolean(value) && typeof value === "object" && !Array.isArray(value) ); const exactKeys = ( value: Record, required: string[], optional: string[], label: string, ): void => { const keys = Object.keys(value); if ( required.some((key) => !keys.includes(key)) || keys.some((key) => !required.includes(key) && !optional.includes(key)) ) { throw new Error(`${label} is not canonical: unsupported or missing fields`); } }; const isoTimestamp = (value: unknown, label: string): string => { if (typeof value !== "string" || !Number.isFinite(Date.parse(value))) { throw new Error(`${label} must be an ISO timestamp`); } return value; }; const manifestDir = (value: unknown, label: string): string => { if (typeof value !== "string" || !SAFE_MANIFEST_DIR.test(value)) { throw new Error(`${label} is not a canonical manifest directory`); } return value; }; const manifestFile = (value: unknown, directory: string, label: string): string => { if ( typeof value !== "string" || !SAFE_MANIFEST_FILE.test(value) || !value.startsWith(`${directory}/`) ) { throw new Error(`${label} must belong to its canonical manifest directory`); } return value; }; export const validateCheckCodeRunReport = (value: unknown): CheckCodeRunReport => { if (!isRecord(value)) throw new Error("CheckCode report must be an object"); exactKeys(value, [ "version", "runId", "status", "artifactHash", "sourceContractInfoPath", "entries", "commands", "startedAt", "completedAt", "reportPath", ], ["deploymentAudit", "effectiveContractInfoPath", "rawCheckReportPath", "error"], "CheckCode report"); if (value.version !== 1) throw new Error("CheckCode report version must be 1"); if (value.sourceContractInfoPath !== "contractInfo.json") { throw new Error("CheckCode report sourceContractInfoPath must be contractInfo.json"); } if (value.deploymentAudit !== undefined) { if (!isRecord(value.deploymentAudit)) { throw new Error("CheckCode report deploymentAudit must be an object"); } exactKeys(value.deploymentAudit, [ "deploymentId", "registryCommitId", "registryCommitPath", "registryCommitHash", "selections", ], [], "CheckCode report deploymentAudit"); if ( typeof value.deploymentAudit.deploymentId !== "string" || typeof value.deploymentAudit.registryCommitId !== "string" || typeof value.deploymentAudit.registryCommitPath !== "string" || !HASH.test(String(value.deploymentAudit.registryCommitHash)) || !Array.isArray(value.deploymentAudit.selections) || value.deploymentAudit.selections.length === 0 ) { throw new Error("CheckCode report deploymentAudit is invalid"); } const expectedPath = `scripts/tasks/${value.deploymentAudit.deploymentId}/results/registry/${value.deploymentAudit.registryCommitId}.json`; if (value.deploymentAudit.registryCommitPath !== expectedPath) { throw new Error("CheckCode report deploymentAudit registry path is invalid"); } const selectionKeys = value.deploymentAudit.selections.map((raw, index) => { if (!isRecord(raw)) throw new Error(`CheckCode deploymentAudit selection ${index} is invalid`); exactKeys(raw, ["network", "executionId"], [], `CheckCode deploymentAudit selection ${index}`); if (typeof raw.network !== "string" || typeof raw.executionId !== "string") { throw new Error(`CheckCode deploymentAudit selection ${index} is invalid`); } return `${raw.network}\0${raw.executionId}`; }); if ( new Set(value.deploymentAudit.selections.map((item) => String((item as Record).network))).size !== value.deploymentAudit.selections.length || JSON.stringify(selectionKeys) !== JSON.stringify([...selectionKeys].sort()) ) { throw new Error("CheckCode deploymentAudit selections must be unique and sorted"); } } if ( value.status !== "compile_failed" && value.status !== "check_failed" && value.status !== "sync_partial" && value.status !== "succeeded" ) { throw new Error("CheckCode report status is invalid"); } if (!Array.isArray(value.entries) || !Array.isArray(value.commands)) { throw new Error("CheckCode report entries and commands must be arrays"); } const entries = value.entries.map((raw, index): CheckCodeEntry => { if (!isRecord(raw)) throw new Error(`CheckCode entry ${index} must be an object`); exactKeys(raw, [ "network", "instanceId", "contractInfoKey", "actualContract", "address", "manifestDir", "manifestFile", "syncStatus", "beforeHash", "afterHash", ], ["proxyKind", "implementationAddress"], `CheckCode entry ${index}`); if ( raw.syncStatus !== "not_required" && raw.syncStatus !== "already_current" && raw.syncStatus !== "synced" && raw.syncStatus !== "proxy_kind_unknown" && raw.syncStatus !== "failed" ) { throw new Error(`CheckCode entry ${index} syncStatus is invalid`); } const entryManifestDir = manifestDir(raw.manifestDir, `CheckCode entry ${index}.manifestDir`); manifestFile(raw.manifestFile, entryManifestDir, `CheckCode entry ${index}.manifestFile`); for (const key of [ "network", "instanceId", "contractInfoKey", "actualContract", "address", "manifestFile", ]) { if (typeof raw[key] !== "string" || !String(raw[key]).trim()) { throw new Error(`CheckCode entry ${index}.${key} is required`); } } if (!HASH.test(String(raw.beforeHash)) || !HASH.test(String(raw.afterHash))) { throw new Error(`CheckCode entry ${index} manifest hash is invalid`); } return raw as unknown as CheckCodeEntry; }); const commands = value.commands.map((raw, index): CommandEvidence => { if (!isRecord(raw)) throw new Error(`CheckCode command ${index} must be an object`); exactKeys(raw, ["command", "args", "status"], ["network", "manifestDir", "manifestFile", "error"], `CheckCode command ${index}`); if (typeof raw.command !== "string" || !Array.isArray(raw.args) || !Number.isInteger(raw.status)) { throw new Error(`CheckCode command ${index} is invalid`); } if ((raw.manifestDir === undefined) !== (raw.manifestFile === undefined)) { throw new Error(`CheckCode command ${index} manifestDir and manifestFile must appear together`); } if (raw.manifestDir !== undefined) { const commandManifestDir = manifestDir(raw.manifestDir, `CheckCode command ${index}.manifestDir`); if (raw.manifestFile !== undefined) { manifestFile(raw.manifestFile, commandManifestDir, `CheckCode command ${index}.manifestFile`); } } return raw as unknown as CommandEvidence; }); if (value.status === "succeeded") { if ( entries.some((entry) => ![ "not_required", "already_current", "synced", ].includes(entry.syncStatus)) || commands.some((command) => command.status !== 0) || value.error !== undefined ) { throw new Error("Succeeded CheckCode report contains unsuccessful evidence"); } } if (value.status !== "succeeded" && (typeof value.error !== "string" || !value.error)) { throw new Error("Unsuccessful CheckCode report requires an error"); } return { ...(value as unknown as CheckCodeRunReport), entries, commands, startedAt: isoTimestamp(value.startedAt, "CheckCode startedAt"), completedAt: isoTimestamp(value.completedAt, "CheckCode completedAt"), }; }; const safeRunId = (runId: string): string => { if (!/^[A-Za-z0-9][A-Za-z0-9_-]*$/.test(runId)) { throw new Error("CheckCode runId contains unsupported characters"); } return runId; }; const safeBindingId = (value: string, label: string): string => { if (!/^[A-Za-z0-9][A-Za-z0-9_-]{0,199}$/.test(value)) { throw new Error(`${label} contains unsupported characters`); } return value; }; const normalizeForJson = (value: unknown): unknown => { if (Array.isArray(value)) return value.map(normalizeForJson); if (isRecord(value)) { return Object.fromEntries( Object.keys(value).sort().map((key) => [key, normalizeForJson(value[key])]), ); } return value; }; const hashValue = (value: unknown): string => crypto .createHash("sha256") .update(JSON.stringify(normalizeForJson(value))) .digest("hex"); const writeJsonAtomic = (filePath: string, value: unknown): void => { fs.mkdirSync(path.dirname(filePath), { recursive: true }); const temporary = `${filePath}.${process.pid}.${Date.now()}.tmp`; try { fs.writeFileSync(temporary, JSON.stringify(value, null, 2) + "\n"); fs.renameSync(temporary, filePath); } catch (error) { if (fs.existsSync(temporary)) fs.unlinkSync(temporary); throw error; } }; const readJson = (filePath: string, label: string): Record => { let value: unknown; try { value = JSON.parse(fs.readFileSync(filePath, "utf8")); } catch (error) { throw new Error(`${label} is invalid: ${error instanceof Error ? error.message : String(error)}`); } if (!isRecord(value)) throw new Error(`${label} must be an object`); return value; }; const prepareDeploymentAudit = ( options: CheckCodeOptions, ): CheckCodeDeploymentAuditEvidence | undefined => { if (!options.deploymentAudit) return undefined; if (options.contracts?.length) { throw new Error("Deployment CheckCode audit cannot filter contracts"); } const deploymentId = safeBindingId( options.deploymentAudit.deploymentId, "deploymentAudit deploymentId", ); const registryCommitId = safeBindingId( options.deploymentAudit.registryCommitId, "deploymentAudit registryCommitId", ); const registryCommitPath = [ "scripts", "tasks", deploymentId, "results", "registry", `${registryCommitId}.json`, ].join("/"); const registryFile = path.join(options.root, ...registryCommitPath.split("/")); const registry = readJson(registryFile, "Registry commit report"); if ( registry.version !== 1 || registry.status !== "committed" || registry.taskId !== deploymentId || registry.commitId !== registryCommitId || !Array.isArray(registry.selections) || registry.selections.length === 0 ) { throw new Error("Registry commit report does not match the deployment audit"); } const selections = registry.selections.map((raw, index) => { if (!isRecord(raw)) throw new Error(`Registry commit selection ${index} is invalid`); return { network: safeBindingId(String(raw.network || ""), `Registry selection ${index} network`), executionId: safeBindingId( String(raw.executionId || ""), `Registry selection ${index} executionId`, ), }; }); const sortedSelections = [...selections].sort((left, right) => left.network.localeCompare(right.network)); if ( new Set(selections.map((selection) => selection.network)).size !== selections.length || JSON.stringify(selections) !== JSON.stringify(sortedSelections) ) { throw new Error("Registry commit selections must be unique and sorted"); } if (options.networks?.length) { const requested = [...new Set(options.networks)].sort(); const committed = selections.map((selection) => selection.network).sort(); if (JSON.stringify(requested) !== JSON.stringify(committed)) { throw new Error("Deployment CheckCode networks must exactly match the registry commit"); } } const contractInfo = readJson(path.join(options.root, "contractInfo.json"), "contractInfo.json"); if (registry.afterHash !== hashValue(contractInfo)) { throw new Error("Formal contractInfo does not match the registry commit"); } const taskDir = path.join(options.root, "scripts", "tasks", deploymentId); const lock = readJson(path.join(taskDir, "parameters.lock.json"), "parameters.lock.json"); if (lock.version !== 2 || lock.deploymentId !== deploymentId || !Array.isArray(lock.executions)) { throw new Error("Deployment CheckCode requires canonical parameters.lock.json"); } const status = readJson(path.join(taskDir, "status.json"), "deployment status.json"); if (status.status !== "deployed_pending_code_audit" || !isRecord(status.networks)) { throw new Error("Deployment is not pending code audit"); } const sourceRecordPaths = Array.isArray(registry.sourceRecordPaths) ? new Set(registry.sourceRecordPaths.map(String)) : new Set(); for (const selection of selections) { const executions = lock.executions.filter((raw) => ( isRecord(raw) && raw.executionId === selection.executionId )); if (executions.length !== 1) { throw new Error(`Deployment lock is missing execution ${selection.executionId}`); } const execution = executions[0]; if ( !isRecord(execution.releaseFingerprint) || execution.releaseFingerprint.artifactHash !== options.artifactHash ) { throw new Error(`CheckCode artifactHash does not match ${selection.network} execution lock`); } const networkStatus = status.networks[selection.network]; if ( !isRecord(networkStatus) || networkStatus.deployStatus !== "deployed_pending_code_audit" || networkStatus.registryExecutionId !== selection.executionId ) { throw new Error(`${selection.network} is not pending audit for the committed execution`); } const recordPath = `scripts/tasks/${deploymentId}/results/${selection.network}.${selection.executionId}.deployment.json`; if (!sourceRecordPaths.has(recordPath)) { throw new Error(`Registry commit does not bind deployment record ${recordPath}`); } const record = readJson(path.join(options.root, ...recordPath.split("/")), "Deployment record"); if ( record.taskId !== deploymentId || record.network !== selection.network || record.executionId !== selection.executionId || record.status !== "applied" ) { throw new Error(`Deployment record is not applied for ${selection.network}`); } } return { deploymentId, registryCommitId, registryCommitPath, registryCommitHash: crypto.createHash("sha256").update(fs.readFileSync(registryFile)).digest("hex"), selections, }; }; const completeDeploymentAudit = ( root: string, report: CheckCodeRunReport, audit: CheckCodeDeploymentAuditEvidence, ): void => { const taskDir = path.join(root, "scripts", "tasks", audit.deploymentId); const statusFile = path.join(taskDir, "status.json"); const reportFile = path.join(root, ...report.reportPath.split("/")); const reportHash = crypto.createHash("sha256").update(fs.readFileSync(reportFile)).digest("hex"); const status = readJson(statusFile, "deployment status.json"); const currentNetworks = isRecord(status.networks) ? status.networks : {}; const networks = { ...currentNetworks }; for (const selection of audit.selections) { const current = currentNetworks[selection.network]; if (!isRecord(current) || current.registryExecutionId !== selection.executionId) { throw new Error(`${selection.network} deployment status changed during CheckCode`); } networks[selection.network] = { ...current, deployStatus: "completed", checkCodeStatus: "succeeded", checkCodeRunId: report.runId, checkCodeReportPath: report.reportPath, updatedAt: report.completedAt, }; } writeJsonAtomic(statusFile, { ...status, status: "completed", networks, codeAudit: { status: "succeeded", runId: report.runId, reportPath: report.reportPath, reportHash, artifactHash: report.artifactHash, registryCommitId: audit.registryCommitId, registryCommitPath: audit.registryCommitPath, registryCommitHash: audit.registryCommitHash, selections: audit.selections, completedAt: report.completedAt, }, updatedAt: report.completedAt, }); }; const directoryHash = (directory: string): string => { if (!fs.existsSync(directory)) return hashValue([]); if (fs.lstatSync(directory).isSymbolicLink()) { throw new Error(`Manifest directory cannot be a symlink: ${directory}`); } const files: string[] = []; const visit = (current: string): void => { for (const name of fs.readdirSync(current).sort()) { const filePath = path.join(current, name); const stat = fs.lstatSync(filePath); if (stat.isSymbolicLink()) throw new Error(`Manifest path cannot contain symlinks: ${filePath}`); if (stat.isDirectory()) visit(filePath); else if (stat.isFile()) files.push(filePath); } }; visit(directory); const hash = crypto.createHash("sha256"); for (const filePath of files) { hash.update(path.relative(directory, filePath).split(path.sep).join("/")); hash.update("\0"); hash.update(fs.readFileSync(filePath)); hash.update("\0"); } return hash.digest("hex"); }; const confinedManifestFile = (input: { root: string; directory: string; requestedFile: string; }): { absolute: string; relative: string } => { const expectedDirectory = path.resolve(input.root, input.directory); const absolute = path.isAbsolute(input.requestedFile) ? path.resolve(input.requestedFile) : path.resolve(input.root, input.requestedFile); if (path.dirname(absolute) !== expectedDirectory) { throw new Error(`Manifest file escapes ${input.directory}: ${input.requestedFile}`); } const name = path.basename(absolute); if (!/^[a-z0-9][a-z0-9-]*\.json$/.test(name)) { throw new Error(`Manifest filename is not canonical: ${name}`); } if (fs.existsSync(absolute)) { const stat = fs.lstatSync(absolute); if (stat.isSymbolicLink()) throw new Error(`Manifest file cannot be a symlink: ${absolute}`); if (!stat.isFile()) throw new Error(`Manifest path is not a regular file: ${absolute}`); } return { absolute, relative: path.relative(input.root, absolute).split(path.sep).join("/"), }; }; const filterContractInfo = ( contractInfo: EffectiveContractInfo, networks: string[] | undefined, contracts: string[] | undefined, ): EffectiveContractInfo => { const networkFilter = networks ? new Set(networks) : undefined; const contractFilter = contracts ? new Set(contracts) : undefined; return Object.fromEntries(Object.entries(contractInfo) .filter(([network]) => !networkFilter || networkFilter.has(network)) .map(([network, entries]) => [ network, Object.fromEntries(Object.entries(entries).filter(([key]) => { if (!contractFilter) return true; const parts = key.split(":"); return contractFilter.has(key) || contractFilter.has(parts[0]) || contractFilter.has(parts.at(-1) || key); })), ]) .filter(([, entries]) => Object.keys(entries).length > 0)); }; const registryEntries = (contractInfo: EffectiveContractInfo): Array<{ network: string; instanceId: string; contractInfoKey: string; actualContract: string; address: string; }> => Object.entries(contractInfo).flatMap(([network, entries]) => ( Object.entries(entries).map(([contractInfoKey, address]) => { const parts = contractInfoKey.split(":"); return { network, instanceId: parts[0], contractInfoKey, actualContract: parts.at(-1) || contractInfoKey, address, }; }) )).sort((left, right) => ( `${left.network}.${left.contractInfoKey}`.localeCompare(`${right.network}.${right.contractInfoKey}`) )); const manifestDocument = (filePath: string): Record => { if (!fs.existsSync(filePath)) return {}; try { const parsed = JSON.parse(fs.readFileSync(filePath, "utf8")); return isRecord(parsed) ? parsed : {}; } catch { return {}; } }; const manifestIsCurrent = (input: { filePath: string; proxyAddress: string; implementationAddress: string; storageLayoutHash: string; }): boolean => { const proxy = input.proxyAddress.toLowerCase(); const implementation = input.implementationAddress.toLowerCase(); let hasProxy = false; let hasImplementation = false; let hasLayout = false; const document = manifestDocument(input.filePath); if (Array.isArray(document.proxies)) { hasProxy = document.proxies.some((item) => ( isRecord(item) && typeof item.address === "string" && item.address.toLowerCase() === proxy )); } if (isRecord(document.impls)) { for (const rawImplementation of Object.values(document.impls)) { if (!isRecord(rawImplementation)) continue; const addresses = [rawImplementation.address] .concat(Array.isArray(rawImplementation.allAddresses) ? rawImplementation.allAddresses : []) .filter((address): address is string => typeof address === "string") .map((address) => address.toLowerCase()); if (!addresses.includes(implementation)) continue; hasImplementation = true; if (rawImplementation.layout !== undefined) { hasLayout = hashValue(rawImplementation.layout) === input.storageLayoutHash; } } } return hasProxy && hasImplementation && hasLayout; }; export const runCheckCode = async ( options: CheckCodeOptions, dependencies: CheckCodeDependencies, ): Promise => { const runId = safeRunId(options.runId); const now = dependencies.now || (() => new Date().toISOString()); const startedAt = now(); const deploymentAudit = prepareDeploymentAudit(options); const runDir = path.join(options.root, "scripts", "checkcode", "runs", runId); const reportFile = path.join(runDir, "report.json"); const effectiveContractInfoFile = path.join(runDir, "effective-contract-info.json"); const rawCheckReportFile = path.join(runDir, "gate-tool-check.json"); const commands: CommandEvidence[] = []; const entries: CheckCodeEntry[] = []; const formalContractInfoPath = path.join(options.root, "contractInfo.json"); const formalContractInfoBefore = fs.existsSync(formalContractInfoPath) ? fs.readFileSync(formalContractInfoPath) : undefined; const assertFormalContractInfoUnchanged = (): void => { const exists = fs.existsSync(formalContractInfoPath); if ( (formalContractInfoBefore === undefined && exists) || (formalContractInfoBefore !== undefined && !exists) || ( formalContractInfoBefore !== undefined && exists && !formalContractInfoBefore.equals(fs.readFileSync(formalContractInfoPath)) ) ) { throw new Error("Formal contractInfo.json changed during CheckCode"); } }; const report = (status: CheckCodeStatus, error?: string): CheckCodeRunReport => { assertFormalContractInfoUnchanged(); const value = validateCheckCodeRunReport({ version: 1, runId, status, artifactHash: options.artifactHash, sourceContractInfoPath: "contractInfo.json", ...(deploymentAudit ? { deploymentAudit } : {}), ...(fs.existsSync(effectiveContractInfoFile) ? { effectiveContractInfoPath: path.relative(options.root, effectiveContractInfoFile).split(path.sep).join("/") } : {}), ...(fs.existsSync(rawCheckReportFile) ? { rawCheckReportPath: path.relative(options.root, rawCheckReportFile).split(path.sep).join("/") } : {}), entries, commands, startedAt, completedAt: now(), ...(error ? { error } : {}), reportPath: path.relative(options.root, reportFile).split(path.sep).join("/"), }); writeJsonAtomic(reportFile, value); return value; }; const runCommand = async ( command: string, args: string[], evidence: { network?: string; manifestDir?: string; manifestFile?: string; env?: NodeJS.ProcessEnv; evidenceArgs?: string[]; } = {}, ): Promise => { let result: CommandResult; try { result = await dependencies.runCommand(command, args, { cwd: options.root, env: evidence.env, }); } catch (error) { result = { status: 1, error: error instanceof Error ? error.message : String(error) }; } commands.push({ command, args: evidence.evidenceArgs || args, status: result.status, ...(evidence.network ? { network: evidence.network } : {}), ...(evidence.manifestDir ? { manifestDir: evidence.manifestDir } : {}), ...(evidence.manifestFile ? { manifestFile: evidence.manifestFile } : {}), ...(result.error ? { error: result.error } : {}), }); return result; }; const compile = await runCommand("npm", ["run", "compile"]); if (compile.status !== 0) return report("compile_failed", compile.error || "Compilation failed"); let contractInfo: EffectiveContractInfo; let deploymentInfo: DeploymentInfo; try { contractInfo = filterContractInfo( dependencies.loadContractInfo(), deploymentAudit ? deploymentAudit.selections.map((selection) => selection.network) : options.networks, options.contracts, ); deploymentInfo = dependencies.loadDeploymentInfo(); writeJsonAtomic(effectiveContractInfoFile, contractInfo); } catch (error) { return report("check_failed", error instanceof Error ? error.message : String(error)); } const selectedEntries = registryEntries(contractInfo); if ( selectedEntries.length === 0 && (Boolean(options.networks?.length) || Boolean(options.contracts?.length)) ) { return report("check_failed", "CheckCode scope contains no contracts"); } const check = await runCommand("npx", [ "--no-install", "gate-tool", "check", "--config", effectiveContractInfoFile, "--output", rawCheckReportFile, ], { evidenceArgs: [ "--no-install", "gate-tool", "check", "--config", path.relative(options.root, effectiveContractInfoFile).split(path.sep).join("/"), "--output", path.relative(options.root, rawCheckReportFile).split(path.sep).join("/"), ], }); if (check.status !== 0) return report("check_failed", check.error || "gate-tool check failed"); for (const selected of selectedEntries) { let resolvedManifestDir: string; try { resolvedManifestDir = manifestDir( dependencies.resolveManifestDir(selected.network), `Manifest directory for ${selected.network}`, ); } catch (error) { return report("sync_partial", error instanceof Error ? error.message : String(error)); } const directory = path.join(options.root, resolvedManifestDir); const beforeHash = directoryHash(directory); let exactManifest: { absolute: string; relative: string }; try { exactManifest = confinedManifestFile({ root: options.root, directory: resolvedManifestDir, requestedFile: await dependencies.resolveManifestFile( selected.network, resolvedManifestDir, ), }); } catch (error) { return report("sync_partial", error instanceof Error ? error.message : String(error)); } const metadata = deploymentInfo.networks[selected.network]?.[selected.instanceId]; let implementationAddress: string | undefined; try { implementationAddress = await dependencies.getImplementation( selected.network, selected.address, ); } catch (error) { entries.push({ ...selected, manifestDir: resolvedManifestDir, manifestFile: exactManifest.relative, syncStatus: "failed", beforeHash, afterHash: directoryHash(directory), }); continue; } const base = { ...selected, actualContract: metadata?.actualContract || selected.actualContract, ...(metadata?.proxyKind ? { proxyKind: metadata.proxyKind } : {}), ...(implementationAddress ? { implementationAddress } : {}), manifestDir: resolvedManifestDir, manifestFile: exactManifest.relative, beforeHash, }; if (metadata?.proxyKind === "uups" && !implementationAddress) { entries.push({ ...base, syncStatus: "failed", afterHash: directoryHash(directory) }); continue; } if (!implementationAddress || metadata?.proxyKind && metadata.proxyKind !== "uups") { entries.push({ ...base, syncStatus: "not_required", afterHash: directoryHash(directory) }); continue; } if (!metadata || metadata.proxyKind !== "uups") { entries.push({ ...base, syncStatus: "proxy_kind_unknown", afterHash: directoryHash(directory) }); continue; } let storageLayoutHash: string; try { storageLayoutHash = await dependencies.getStorageLayoutHash(metadata.actualContract); } catch (error) { entries.push({ ...base, syncStatus: "failed", afterHash: directoryHash(directory) }); continue; } if (manifestIsCurrent({ filePath: exactManifest.absolute, proxyAddress: selected.address, implementationAddress, storageLayoutHash, })) { entries.push({ ...base, syncStatus: "already_current", afterHash: beforeHash }); continue; } const sync = await runCommand("npx", [ "--no-install", "gate-tool", "sync", "--proxy", selected.address, "--contract", metadata.actualContract, "--network", selected.network, "--skip-tx-hash", ], { network: selected.network, manifestDir: resolvedManifestDir, manifestFile: exactManifest.relative, env: { ...process.env, GATE_WORKFLOW_TARGET: selected.network, MANIFEST_DEFAULT_DIR: resolvedManifestDir, }, }); const afterHash = directoryHash(directory); entries.push({ ...base, syncStatus: sync.status === 0 && afterHash !== beforeHash ? "synced" : "failed", afterHash, }); } if (entries.some((entry) => ( entry.syncStatus === "failed" || entry.syncStatus === "proxy_kind_unknown" ))) { return report("sync_partial", "One or more manifest outcomes require review"); } const checkedAt = now(); updateCheckCodeEvidenceIndex( options.root, entries.map((entry): CheckCodeEvidenceEntry => ({ status: "passed", network: entry.network, instanceId: entry.instanceId, contractInfoKey: entry.contractInfoKey, address: entry.address, artifactHash: options.artifactHash, runId, checkedAt, })), ); const succeeded = report("succeeded"); if (deploymentAudit) completeDeploymentAudit(options.root, succeeded, deploymentAudit); return succeeded; };