Download scripts/deploy.js from lablab-ai-amd-developer-hackathon/proofbridge-liner-safety-kernel: direct link, hf CLI and curl.
- Browser
- Download file 9.78 kB
-
https://huggingface.co/spaces/lablab-ai-amd-developer-hackathon/proofbridge-liner-safety-kernel/resolve/main/scripts/deploy.js
- Command line
-
hf download hf://spaces/lablab-ai-amd-developer-hackathon/proofbridge-liner-safety-kernel/scripts/deploy.js
-
curl -L -o deploy.js https://huggingface.co/spaces/lablab-ai-amd-developer-hackathon/proofbridge-liner-safety-kernel/resolve/main/scripts/deploy.js
9.78 kB
| /** | |
| * scripts/deploy.js | |
| * ProofBridge Liner β Foundry deployment runner | |
| * | |
| * Usage: | |
| * node scripts/deploy.js [--target cb|full] [--dry-run] [--rpc <url>] | |
| * | |
| * Targets: | |
| * cb Deploy CircuitBreaker only (Phase 2 MVP) | |
| * full Deploy CircuitBreaker + AssetRegistry + TEEVerifier (default) | |
| * | |
| * Required env vars: | |
| * PRIVATE_KEY Deployer private key (0x-prefixed) | |
| * POLYGON_AMOY_RPC_URL RPC endpoint | |
| * ORACLE_ADDRESS Single-oracle address (for CircuitBreaker) | |
| * ENCLAVE_ADDRESS TEE enclave public key β address form (full only) | |
| * | |
| * On success writes deployed addresses to .env.deployed and prints a summary. | |
| */ | |
| ; | |
| const { execSync, spawnSync } = require('child_process'); | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| // Load notifier if available (optional dependency) | |
| let notifier; | |
| try { | |
| notifier = require('../prover/notifier'); | |
| } catch (_) { | |
| // Notifier not yet available β skip notifications | |
| } | |
| const ROOT = path.resolve(__dirname, '..'); | |
| const FORGE = path.join(ROOT, '.config', '.foundry', 'bin', 'forge'); | |
| const SCRIPTS = { | |
| cb: 'script/DeployCircuitBreaker.s.sol:DeployCircuitBreaker', | |
| full: 'script/DeployFull.s.sol:DeployFull', | |
| }; | |
| const ADDRESS_PATTERNS = { | |
| CircuitBreaker: /CircuitBreaker[^\n]*?(0x[0-9a-fA-F]{40})/, | |
| AssetRegistry: /AssetRegistry[^\n]*?(0x[0-9a-fA-F]{40})/, | |
| TEEVerifier: /TEEVerifier[^\n]*?(0x[0-9a-fA-F]{40})/, | |
| }; | |
| // βββ Argument parsing ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const argv = process.argv.slice(2); | |
| const flag = (f) => argv.includes(f); | |
| const param = (f, d) => { const i = argv.indexOf(f); return i !== -1 ? argv[i + 1] : d; }; | |
| const target = param('--target', 'full'); | |
| const dryRun = flag('--dry-run') || flag('--simulate'); | |
| const rpcUrl = param('--rpc', process.env.POLYGON_AMOY_RPC_URL || ''); | |
| if (!SCRIPTS[target]) { | |
| die(`Unknown target "${target}". Use --target cb or --target full.`); | |
| } | |
| // βββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function die(msg) { | |
| console.error(`\n[deploy] ERROR: ${msg}\n`); | |
| process.exit(1); | |
| } | |
| function ok(msg) { console.log(` β ${msg}`); } | |
| function info(msg) { console.log(` Β· ${msg}`); } | |
| function step(msg) { console.log(`\n[deploy] ${msg}`); } | |
| function hr() { console.log('\n' + 'β'.repeat(62)); } | |
| // βββ Pre-flight checks βββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| step('Pre-flight checks'); | |
| if (!fs.existsSync(FORGE)) { | |
| if (notifier) notifier.deploymentFailure({ | |
| target, | |
| error: 'forge not found', | |
| detail: 'Install Foundry', | |
| }); | |
| die( | |
| `forge not found at ${FORGE}.\n` + | |
| ' Install Foundry: curl -L https://foundry.paradigm.xyz | bash && foundryup\n' + | |
| ' Or run the project setup once to install it locally.' | |
| ); | |
| } | |
| ok(`forge found at ${FORGE}`); | |
| const required = ['PRIVATE_KEY', 'POLYGON_AMOY_RPC_URL', 'ORACLE_ADDRESS']; | |
| if (target === 'full') required.push('ENCLAVE_ADDRESS'); | |
| const missing = required.filter(v => !process.env[v]); | |
| if (missing.length) { | |
| die( | |
| `Missing required environment variables:\n` + | |
| missing.map(v => ` β’ ${v}`).join('\n') + '\n\n' + | |
| ' Set them in Replit Secrets (or export them locally) before deploying.' | |
| ); | |
| } | |
| ok(`All required env vars present (${required.join(', ')})`); | |
| if (!rpcUrl) { | |
| if (notifier) notifier.deploymentFailure({ target, error: 'Empty RPC URL' }); | |
| die('POLYGON_AMOY_RPC_URL is empty.'); | |
| } | |
| ok(`RPC: ${rpcUrl.replace(/\/\/.+@/, '//***@')}`); | |
| // Notify deployment start (pre-flight cleared) | |
| if (notifier) { | |
| notifier.deploymentStart({ | |
| target: target === 'full' | |
| ? 'CircuitBreaker + AssetRegistry + TEEVerifier' | |
| : 'CircuitBreaker', | |
| network: 'Polygon Amoy', | |
| rpc: rpcUrl, | |
| }); | |
| } | |
| // βββ Build (compile) βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| step('Compiling contracts'); | |
| const buildResult = spawnSync( | |
| FORGE, ['build', '--silent'], | |
| { cwd: ROOT, encoding: 'utf8', env: process.env } | |
| ); | |
| if (buildResult.status !== 0) { | |
| console.error(buildResult.stderr); | |
| die('forge build failed. Fix compilation errors first.'); | |
| } | |
| ok('All contracts compiled successfully'); | |
| // βββ Deploy βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const scriptPath = SCRIPTS[target]; | |
| step(`Deploying: ${scriptPath} ${dryRun ? '(DRY-RUN / simulate)' : 'to Polygon Amoy'}`); | |
| const forgeArgs = [ | |
| 'script', scriptPath, | |
| '--rpc-url', rpcUrl, | |
| '--private-key', process.env.PRIVATE_KEY, | |
| '--gas-price', '30000000000', // 30 gwei minimum for Amoy | |
| '-vvv', | |
| ]; | |
| if (!dryRun) { | |
| forgeArgs.push('--broadcast'); | |
| info('Broadcasting transactions on-chain (--broadcast)'); | |
| } else { | |
| info('Simulating only β no transactions will be sent'); | |
| } | |
| info(`Running: forge ${forgeArgs.filter(a => a !== process.env.PRIVATE_KEY).join(' ')}`); | |
| const deployResult = spawnSync(FORGE, forgeArgs, { | |
| cwd: ROOT, | |
| encoding: 'utf8', | |
| env: { | |
| ...process.env, | |
| ORACLE_ADDRESS: process.env.ORACLE_ADDRESS, | |
| ENCLAVE_ADDRESS: process.env.ENCLAVE_ADDRESS || '', | |
| }, | |
| maxBuffer: 4 * 1024 * 1024, | |
| }); | |
| const stdout = deployResult.stdout || ''; | |
| const stderr = deployResult.stderr || ''; | |
| const combined = stdout + '\n' + stderr; | |
| if (deployResult.status !== 0) { | |
| console.error('\nββ forge output ββββββββββββββββββββββββββββββββββββββ'); | |
| console.error(combined); | |
| die('forge script failed. See output above.'); | |
| } | |
| console.log('\nββ forge output ββββββββββββββββββββββββββββββββββββββ'); | |
| console.log(combined.trim()); | |
| // βββ Parse addresses βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| step('Parsing deployed addresses'); | |
| const deployed = {}; | |
| for (const [name, pattern] of Object.entries(ADDRESS_PATTERNS)) { | |
| const m = combined.match(pattern); | |
| if (m) { | |
| deployed[name] = m[1]; | |
| ok(`${name.padEnd(18)} ${m[1]}`); | |
| } else { | |
| console.log(`[debug] pattern for ${name} did not match. pattern=${pattern}`); | |
| } | |
| } | |
| if (!Object.keys(deployed).length) { | |
| info('No addresses parsed (simulation may not emit them β check forge output above)'); | |
| } | |
| // βββ Write .env.deployed βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if (!dryRun && Object.keys(deployed).length) { | |
| step('Writing .env.deployed'); | |
| const envLines = [ | |
| `# ProofBridge Liner β deployed addresses`, | |
| `# Generated by scripts/deploy.js on ${new Date().toISOString()}`, | |
| `# Network: Polygon Amoy (chainId 80002)`, | |
| '', | |
| ]; | |
| if (deployed.CircuitBreaker) envLines.push(`CIRCUIT_BREAKER_ADDRESS=${deployed.CircuitBreaker}`); | |
| if (deployed.AssetRegistry) envLines.push(`ASSET_REGISTRY_ADDRESS=${deployed.AssetRegistry}`); | |
| if (deployed.TEEVerifier) envLines.push(`TEE_VERIFIER_ADDRESS=${deployed.TEEVerifier}`); | |
| if (process.env.ORACLE_ADDRESS) envLines.push(`ORACLE_ADDRESS=${process.env.ORACLE_ADDRESS}`); | |
| if (process.env.ENCLAVE_ADDRESS) envLines.push(`ENCLAVE_ADDRESS=${process.env.ENCLAVE_ADDRESS}`); | |
| envLines.push(''); | |
| const envFile = path.join(ROOT, '.env.deployed'); | |
| fs.writeFileSync(envFile, envLines.join('\n')); | |
| ok(`.env.deployed written to ${envFile}`); | |
| info('Copy these values into Replit Secrets so the dashboard picks them up.'); | |
| } | |
| // βββ Summary βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| hr(); | |
| console.log('\n DEPLOYMENT SUMMARY\n'); | |
| console.log(` Target : ${target === 'full' ? 'Full suite (CB + Registry + TEE)' : 'CircuitBreaker only'}`); | |
| console.log(` Mode : ${dryRun ? 'Simulate (dry-run)' : 'Live broadcast'}`); | |
| console.log(` Network : Polygon Amoy (chainId 80002)`); | |
| console.log(` RPC : ${rpcUrl.replace(/\/\/.+@/, '//***@')}`); | |
| console.log(''); | |
| if (Object.keys(deployed).length) { | |
| if (notifier) notifier.deploymentSuccess({ | |
| target, | |
| network: 'Polygon Amoy', | |
| deployed, | |
| }); | |
| for (const [name, addr] of Object.entries(deployed)) { | |
| const scanUrl = `https://amoy.polygonscan.com/address/${addr}`; | |
| console.log(` ${name.padEnd(18)} ${addr}`); | |
| console.log(` ${''.padEnd(18)} ${scanUrl}`); | |
| console.log(''); | |
| } | |
| } else { | |
| console.log(' No addresses extracted (check forge output above)'); | |
| } | |
| if (!dryRun) { | |
| console.log(' Next steps:'); | |
| console.log(' 1. Copy addresses from .env.deployed into Replit Secrets'); | |
| console.log(' 2. Restart the dashboard (npm run start) to reflect new addresses'); | |
| if (target === 'full') { | |
| console.log(' 3. Call AssetRegistry.registerAsset() for each monitored deed'); | |
| console.log(' 4. Run node prover/submitter.js to push first proof hashes'); | |
| } | |
| } | |
| hr(); | |
| console.log(''); | |