From dc9dfd8a8cd541eec8098066922ea8c9f03503c7 Mon Sep 17 00:00:00 2001 From: edwardfavour998-code Date: Mon, 19 Oct 2026 12:33:41 +0100 Subject: [PATCH] Improve contract deployment scripts with testnet hardcoding, deprecation, and resume support Closes #21, #22, #23, #24 - Hard-code testnet as the default network in deploy-contracts.cjs with clear documentation - Add deprecation notices to bash scripts 03 and 04, directing users to Node.js script - Increase MIN_BALANCE_STROOPS from 1 XLM to 10 XLM to properly accommodate 3 WASM uploads - Implement resume functionality to detect and skip already-deployed contracts on retry --- scripts/03-deploy-contracts.sh | 4 ++ scripts/04-init-contracts.sh | 4 ++ scripts/deploy-contracts.cjs | 71 +++++++++++++++++++++++++++++----- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/scripts/03-deploy-contracts.sh b/scripts/03-deploy-contracts.sh index 68f59a9..9bbe891 100644 --- a/scripts/03-deploy-contracts.sh +++ b/scripts/03-deploy-contracts.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash # SynapsVault — Step 3: Build + Deploy all 3 Soroban contracts to Stellar testnet # +# ⚠️ DEPRECATED: Use scripts/deploy-contracts.cjs instead. +# This bash script is maintained for backwards compatibility but will be removed in a future release. +# See deploy-contracts.cjs for enhanced features (resume support, better error handling, mainnet safety). +# # Prerequisites: # - Rust + wasm32 target: rustup target add wasm32-unknown-unknown # - Stellar CLI: https://developers.stellar.org/docs/tools/cli diff --git a/scripts/04-init-contracts.sh b/scripts/04-init-contracts.sh index 3e2e466..69717b8 100644 --- a/scripts/04-init-contracts.sh +++ b/scripts/04-init-contracts.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash # SynapsVault — Step 4: Initialise all deployed contracts # +# ⚠️ DEPRECATED: Use scripts/deploy-contracts.cjs instead. +# This bash script is maintained for backwards compatibility but will be removed in a future release. +# The Node.js script handles both deployment and initialization together. +# # Prerequisites: # source contract-ids.env (sets STELLAR_CONTRACT_VAULT_REGISTRY, # STELLAR_CONTRACT_ACCESS_LEASE, STELLAR_CONTRACT_SUBSCRIPTION) diff --git a/scripts/deploy-contracts.cjs b/scripts/deploy-contracts.cjs index b787c0f..cec35c0 100644 --- a/scripts/deploy-contracts.cjs +++ b/scripts/deploy-contracts.cjs @@ -8,16 +8,22 @@ * Soroban network, initializes each with the admin key, writes the resulting * contract IDs to contract-ids.env, and verifies each contract on-chain. * + * ⚠️ TESTNET IS THE HARD-CODED DEFAULT NETWORK. + * While this script can deploy to mainnet via the --network or NETWORK flag, + * testnet is the intended primary target. Mainnet deployments require explicit + * confirmation and are only available with the --yes flag in non-interactive mode. + * * Usage: - * node scripts/deploy-contracts.cjs [--network testnet|mainnet] [--wasm-dir ] [--yes] [--dry-run] + * node scripts/deploy-contracts.cjs [--network testnet|mainnet|futurenet] [--wasm-dir ] [--yes] [--dry-run] * * Required environment variables: * STELLAR_SECRET_KEY - Secret key of the deploying account. - * NETWORK - Target network (testnet|mainnet). Overridden by --network. + * NETWORK - Target network (testnet|mainnet|futurenet). Overridden by --network. + * Defaults to testnet if not specified. * * Optional environment variables: * SOROBAN_RPC_URL - Soroban RPC endpoint (defaults to the public testnet - * RPC for testnet; required for mainnet). + * RPC for testnet; required for mainnet/futurenet). * CONTRACTS_WASM_DIR - Directory containing the built *.wasm files. Overridden * by --wasm-dir. Defaults to ./target/wasm32-unknown-unknown/release. * ADMIN_PUBLIC_KEY - Address passed to each contract's init(admin). Defaults @@ -78,7 +84,8 @@ function contractEnvName(contractName) { } // Minimum account balance (in stroops) required to proceed with deployment. -const MIN_BALANCE_STROOPS = 10_000_000n; // 1 XLM +// 10 XLM accommodates uploading 3 WASM contracts (each ~300-500KB). +const MIN_BALANCE_STROOPS = 100_000_000n; // 10 XLM // --------------------------------------------------------------------------- // CLI argument parsing @@ -162,10 +169,10 @@ function validateEnv(args) { missing.push('STELLAR_SECRET_KEY'); } - const network = args.network || process.env.NETWORK; - if (!network) { - missing.push('NETWORK (or pass --network)'); - } else if (!NETWORK_PASSPHRASES[network]) { + // Default to testnet if network is not specified (testnet is hard-coded default). + const network = args.network || process.env.NETWORK || 'testnet'; + + if (!NETWORK_PASSPHRASES[network]) { if (!process.env.SOROBAN_RPC_URL) { throw new Error(`Invalid NETWORK value: ${network} (expected testnet|mainnet|futurenet or set SOROBAN_RPC_URL for local)`); } @@ -515,6 +522,38 @@ async function verifyContract({ server, contractId, dryRun, contractName, expect } } +// --------------------------------------------------------------------------- +// Resume logic +// --------------------------------------------------------------------------- + +function loadExistingContractIds() { + if (!fs.existsSync(CONTRACT_IDS_FILE)) { + return {}; + } + + try { + const content = fs.readFileSync(CONTRACT_IDS_FILE, { encoding: 'utf8' }); + const existing = {}; + for (const line of content.split('\n')) { + if (line.startsWith('#') || !line.trim()) { + continue; + } + const [envName, contractId] = line.split('='); + if (envName && contractId) { + const contractName = envName + .replace(/^STELLAR_CONTRACT_/, '') + .toLowerCase() + .replace(/_/g, '-'); + existing[contractName] = contractId; + } + } + return existing; + } catch (err) { + log.warn(`Could not load existing contract IDs: ${err.message}`); + return {}; + } +} + // --------------------------------------------------------------------------- // Output file // --------------------------------------------------------------------------- @@ -598,9 +637,21 @@ async function main() { await preflightChecks({ server, keypair, network, contracts, dryRun: args.dryRun }); log.info(`Admin address for init(): ${adminAddress}`); - const contractIds = {}; - for (const contract of contracts) { + // Load existing contract IDs (for resume support) + const existingIds = loadExistingContractIds(); + const contractIds = { ...existingIds }; + + const contractsToDeploy = contracts.filter((c) => !existingIds[c.name]); + + if (contractsToDeploy.length < contracts.length) { + log.step(`Resuming: ${contractsToDeploy.length}/${contracts.length} contracts need deployment`); + for (const name of Object.keys(existingIds)) { + log.info(`Skipping ${name} (already deployed at ${existingIds[name]})`); + } + } + + for (const contract of contractsToDeploy) { const wasmHash = await uploadWasm({ server, keypair,