From 42c1a498b2423909e9a72e49fdef8de6130faf0d Mon Sep 17 00:00:00 2001 From: nijoe1 Date: Fri, 30 Jan 2026 18:13:54 +0200 Subject: [PATCH] fix(subgraph): use deployments.json as source of truth for addresses - Refactor generate-constants.js and generate-config.js to read contract - addresses from service_contracts/deployments.json instead of the manual - config/network.json. This ensures subgraph configuration stays in sync when contracts are redeployed. --- subgraph/scripts/generate-config.js | 1 + subgraph/scripts/generate-constants.js | 3 +- subgraph/scripts/utils/config-loader.js | 206 ++++++++++++++++++++--- subgraph/templates/constants.template.ts | 2 +- 4 files changed, 189 insertions(+), 23 deletions(-) diff --git a/subgraph/scripts/generate-config.js b/subgraph/scripts/generate-config.js index 07b7dde9..cf13f55b 100644 --- a/subgraph/scripts/generate-config.js +++ b/subgraph/scripts/generate-config.js @@ -34,6 +34,7 @@ if (shouldGenerateYaml) { try { fs.writeFileSync(outputPath, yamlContent); console.log(`✅ Generated subgraph.yaml for ${network} network at: ${outputPath}`); + console.log(` Source: service_contracts/deployments.json`); } catch (error) { console.error(`Error: Failed to write subgraph.yaml to: ${outputPath}`); console.error(`Write Error: ${error.message}`); diff --git a/subgraph/scripts/generate-constants.js b/subgraph/scripts/generate-constants.js index dcc504c5..cec037d5 100644 --- a/subgraph/scripts/generate-constants.js +++ b/subgraph/scripts/generate-constants.js @@ -18,7 +18,7 @@ const requiredContracts = ["PDPVerifier", "ServiceProviderRegistry", "FilecoinWa for (const contract of requiredContracts) { if (!selectedConfig[contract] || !selectedConfig[contract].address) { console.error(`Error: Missing or invalid '${contract}' configuration for network '${network}'`); - console.error(`Each contract must have an 'address' field in config/network.json`); + console.error(`Contract must have an 'address' field in service_contracts/deployments.json`); process.exit(1); } } @@ -50,6 +50,7 @@ try { fs.writeFileSync(outputPath, constantsContent); console.log(`✅ Generated constants for ${network} network at: ${outputPath}`); + console.log(` Source: service_contracts/deployments.json`); } catch (error) { console.error(`Error: Failed to write constants file to: ${outputPath}`); console.error(`Write Error: ${error.message}`); diff --git a/subgraph/scripts/utils/config-loader.js b/subgraph/scripts/utils/config-loader.js index ec86be9c..af9ad399 100644 --- a/subgraph/scripts/utils/config-loader.js +++ b/subgraph/scripts/utils/config-loader.js @@ -1,48 +1,212 @@ const fs = require("fs"); const path = require("path"); +// Network name to chain ID mapping +const NETWORK_CHAIN_IDS = { + mainnet: "314", + calibration: "314159", +}; + +// Network name to subgraph network name mapping +const NETWORK_NAMES = { + mainnet: "filecoin", + calibration: "filecoin-testnet", +}; + +// Mapping from deployments.json keys to template keys +const ADDRESS_MAPPING = { + PDP_VERIFIER_PROXY_ADDRESS: "PDPVerifier", + SERVICE_PROVIDER_REGISTRY_PROXY_ADDRESS: "ServiceProviderRegistry", + FWSS_PROXY_ADDRESS: "FilecoinWarmStorageService", + FILECOIN_PAY_ADDRESS: "USDFCToken", +}; + +// Default start blocks for each network (can be overridden via environment or config) +// These represent the approximate deployment blocks for the contracts +const DEFAULT_START_BLOCKS = { + mainnet: { + PDPVerifier: 1000000, + ServiceProviderRegistry: 1000000, + FilecoinWarmStorageService: 1000000, + USDFCToken: 1000000, + }, + calibration: { + PDPVerifier: 2988297, + ServiceProviderRegistry: 2988311, + FilecoinWarmStorageService: 2988329, + USDFCToken: 2988000, + }, +}; + /** - * Loads and validates network configuration from config/network.json - * @param {string} network - The network name to load - * @returns {Object} The network configuration object + * Loads deployment addresses from service_contracts/deployments.json + * @returns {Object} The parsed deployments object */ -function loadNetworkConfig(network = "calibration") { - const configPath = path.join(__dirname, "..", "..", "config", "network.json"); - let networkConfig; +function loadDeployments() { + const deploymentsPath = path.join( + __dirname, + "..", + "..", + "..", + "service_contracts", + "deployments.json" + ); try { - const configContent = fs.readFileSync(configPath, "utf8"); - networkConfig = JSON.parse(configContent); + const content = fs.readFileSync(deploymentsPath, "utf8"); + return JSON.parse(content); } catch (error) { if (error.code === "ENOENT") { - console.error(`Error: Configuration file not found at: ${configPath}`); - console.error("Please ensure config/network.json exists in your project."); + console.error(`Error: Deployments file not found at: ${deploymentsPath}`); + console.error( + "Please ensure service_contracts/deployments.json exists." + ); process.exit(1); } if (error instanceof SyntaxError) { - console.error(`Error: Invalid JSON in configuration file: ${configPath}`); - console.error("Please check that config/network.json contains valid JSON."); + console.error( + `Error: Invalid JSON in deployments file: ${deploymentsPath}` + ); console.error(`JSON Error: ${error.message}`); } else { - console.error(`Error reading configuration file: ${configPath}`); + console.error(`Error reading deployments file: ${deploymentsPath}`); console.error(`File Error: ${error.message}`); } process.exit(1); } +} + +/** + * Loads optional start block overrides from config/start-blocks.json + * @param {string} network - The network name + * @returns {Object|null} The start blocks object or null if not found + */ +function loadStartBlockOverrides(network) { + const overridesPath = path.join( + __dirname, + "..", + "..", + "config", + "start-blocks.json" + ); + + try { + const content = fs.readFileSync(overridesPath, "utf8"); + const overrides = JSON.parse(content); + return overrides[network] || null; + } catch { + // Start block overrides are optional + return null; + } +} + +/** + * Loads and validates network configuration from service_contracts/deployments.json + * @param {string} network - The network name to load ("mainnet" or "calibration") + * @returns {Object} The network configuration object formatted for templates + */ +function loadNetworkConfig(network = "calibration") { + const chainId = NETWORK_CHAIN_IDS[network]; - if (!networkConfig.networks) { - console.error("Error: Invalid configuration structure. Missing 'networks' object in config/network.json"); - console.error('Expected structure: { "networks": { "calibration": {...}, "mainnet": {...} } }'); + if (!chainId) { + console.error(`Error: Unknown network '${network}'`); + console.error( + `Available networks: ${Object.keys(NETWORK_CHAIN_IDS).join(", ")}` + ); process.exit(1); } - if (!networkConfig.networks[network]) { - console.error(`Error: Network '${network}' not found in config/network.json`); - console.error(`Available networks: ${Object.keys(networkConfig.networks).join(", ")}`); + const deployments = loadDeployments(); + const networkDeployments = deployments[chainId]; + + if (!networkDeployments) { + console.error( + `Error: No deployments found for chain ID ${chainId} (network: ${network})` + ); + console.error(`Available chain IDs: ${Object.keys(deployments).join(", ")}`); process.exit(1); } - return networkConfig.networks[network]; + // Load start block overrides (optional) + const startBlockOverrides = loadStartBlockOverrides(network); + const defaultStartBlocks = DEFAULT_START_BLOCKS[network] || {}; + + // Build the configuration object expected by templates + const config = { + name: NETWORK_NAMES[network], + }; + + // Map deployment addresses to template format + for (const [deploymentKey, templateKey] of Object.entries(ADDRESS_MAPPING)) { + const address = networkDeployments[deploymentKey]; + + if (!address) { + console.error( + `Error: Missing '${deploymentKey}' in deployments.json for chain ID ${chainId}` + ); + process.exit(1); + } + + // Get start block from overrides, defaults, or fallback + const startBlock = + startBlockOverrides?.[templateKey] || + defaultStartBlocks[templateKey] || + 0; + + config[templateKey] = { + address: address, + startBlock: startBlock, + }; + } + + return config; +} + +/** + * Gets the path to an ABI file + * @param {string} contractName - The contract name (e.g., "PDPVerifier") + * @returns {string} The absolute path to the ABI file + */ +function getAbiPath(contractName) { + return path.join( + __dirname, + "..", + "..", + "..", + "service_contracts", + "abi", + `${contractName}.abi.json` + ); +} + +/** + * Loads an ABI from service_contracts/abi/ + * @param {string} contractName - The contract name (e.g., "PDPVerifier") + * @returns {Array} The parsed ABI array + */ +function loadAbi(contractName) { + const abiPath = getAbiPath(contractName); + + try { + const content = fs.readFileSync(abiPath, "utf8"); + return JSON.parse(content); + } catch (error) { + if (error.code === "ENOENT") { + console.error(`Error: ABI file not found at: ${abiPath}`); + console.error( + `Please ensure service_contracts/abi/${contractName}.abi.json exists.` + ); + process.exit(1); + } + if (error instanceof SyntaxError) { + console.error(`Error: Invalid JSON in ABI file: ${abiPath}`); + console.error(`JSON Error: ${error.message}`); + } else { + console.error(`Error reading ABI file: ${abiPath}`); + console.error(`File Error: ${error.message}`); + } + process.exit(1); + } } -module.exports = { loadNetworkConfig }; +module.exports = { loadNetworkConfig, loadDeployments, loadAbi, getAbiPath }; diff --git a/subgraph/templates/constants.template.ts b/subgraph/templates/constants.template.ts index dbfbe542..218d529a 100644 --- a/subgraph/templates/constants.template.ts +++ b/subgraph/templates/constants.template.ts @@ -1,5 +1,5 @@ // This file is auto-generated. Do not edit manually. -// Generated from config/network.json for network: {{network}} +// Generated from service_contracts/deployments.json for network: {{network}} // Last generated: {{timestamp}} import { Address, Bytes } from "@graphprotocol/graph-ts";