diff --git a/.contract-config.md b/.contract-config.md new file mode 100644 index 00000000..e487044d --- /dev/null +++ b/.contract-config.md @@ -0,0 +1,58 @@ +# Contract Configuration File + +This file (`contract-config.json`) is the single source of truth for all contract configuration in the SprintFund project. + +## Fields + +### version +The contract version number (as a string). Currently "3" for sprintfund-core-v3. + +### contract.address +The Stacks address that deployed the contract. This is the contract owner address. + +### contract.name +The name of the active contract. Must match the deployed contract name exactly. + +### contract.principal +The full contract principal in the format `address.name`. This is automatically constructed but included for convenience. + +### network.default +The default network to use when no network is specified. Should be "mainnet" for production. + +### network.mainnet +Configuration for Stacks mainnet including API and explorer URLs. + +### network.testnet +Configuration for Stacks testnet including API and explorer URLs. + +### legacy +Information about deprecated contract versions. Used for migration scripts and documentation. + +## Usage + +### JavaScript/Node.js +```javascript +import { getContractAddress, getContractName } from './scripts/lib/contract-config.js'; +``` + +### TypeScript/Frontend +```typescript +import { config } from './frontend/src/lib/contract-config'; +``` + +### Shell Scripts +```bash +CONTRACT_ADDRESS=$(jq -r '.contract.address' contract-config.json) +CONTRACT_NAME=$(jq -r '.contract.name' contract-config.json) +``` + +## Updating + +When updating contract configuration: + +1. Edit this file with new values +2. Run `npm run validate-config` to verify consistency +3. Update any documentation that references the old values +4. Commit the changes + +See `CONFIGURATION.md` for more details. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..3512ae4b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{js,ts,tsx,json}] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.json] +insert_final_newline = false diff --git a/.github/workflows/validate-config.yml b/.github/workflows/validate-config.yml index b9e4ec6b..1e2c4555 100644 --- a/.github/workflows/validate-config.yml +++ b/.github/workflows/validate-config.yml @@ -1,4 +1,4 @@ -name: Validate Configuration +name: Validate Contract Configuration on: pull_request: @@ -13,9 +13,6 @@ on: - main paths: - 'contract-config.json' - - 'frontend/src/config.ts' - - 'frontend/config.ts' - - '.env.example' jobs: validate: @@ -39,10 +36,3 @@ jobs: run: | cd scripts npm run validate-config - - - name: Check for config drift - if: failure() - run: | - echo "Configuration validation failed!" - echo "Please ensure all config files reference the centralized contract-config.json" - exit 1 diff --git a/CHANGELOG_CONFIG.md b/CHANGELOG_CONFIG.md new file mode 100644 index 00000000..155e6940 --- /dev/null +++ b/CHANGELOG_CONFIG.md @@ -0,0 +1,41 @@ +# Configuration System Changelog + +## [1.0.0] - 2026-04-28 + +### Added +- Centralized contract configuration file (`contract-config.json`) +- Configuration loader utilities for JavaScript and TypeScript +- Validation script to check config consistency across files +- Update script to propagate config changes +- Comprehensive documentation (CONFIGURATION.md, MIGRATION_CONFIG.md) +- TypeScript definitions for config loader +- Unit tests for config loader functions +- GitHub Actions workflow for automated validation +- Helper functions for network and legacy contract management + +### Changed +- Updated `frontend/src/config.ts` to use centralized config as fallback +- Fixed `frontend/config.ts` to use correct contract name (v3) +- Updated all scripts to load from centralized config: + - `scripts/create-proposal.js` + - `scripts/stake.js` + - `scripts/call-logger.js` + - `scripts/withdraw-legacy.js` +- Updated `create-test-proposal.sh` to load from centralized config +- Updated `.env.example` to include contract name +- Updated `CONTRACT_VERSIONS.md` to reference centralized config +- Updated `scripts/README.md` with configuration section + +### Fixed +- Contract name drift between root and src config files +- Hardcoded contract values in multiple scripts +- Missing contract name in environment example +- Inconsistent configuration across the codebase + +### Benefits +- Single source of truth prevents configuration drift +- Easy updates by changing one file +- Automated validation catches mismatches +- Clear documentation for configuration management +- Type-safe configuration access +- Reduced maintenance burden for version updates diff --git a/scripts/.npmrc b/scripts/.npmrc new file mode 100644 index 00000000..b6f27f13 --- /dev/null +++ b/scripts/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/scripts/lib/contract-config.d.ts b/scripts/lib/contract-config.d.ts index 85b76784..ef804ddb 100644 --- a/scripts/lib/contract-config.d.ts +++ b/scripts/lib/contract-config.d.ts @@ -34,5 +34,10 @@ export function getContractName(): string; export function getContractPrincipal(): string; export function getNetworkConfig(networkName?: string): { apiUrl: string; explorerUrl: string }; export function getContractVersion(): string; +export function getLegacyContractName(version: string): string | null; +export function isLegacyContract(contractName: string): boolean; +export function getAllNetworks(): string[]; +export function getDefaultNetwork(): string; + export function getLegacyContractName(version: string | number): string | null; export function isLegacyContract(contractName: string): boolean; diff --git a/scripts/lib/contract-config.js b/scripts/lib/contract-config.js index 463c9e61..8ebd0da9 100644 --- a/scripts/lib/contract-config.js +++ b/scripts/lib/contract-config.js @@ -70,3 +70,13 @@ export function isLegacyContract(contractName) { const legacyNames = Object.values(config.legacy || {}).map(v => v.name); return legacyNames.includes(contractName); } + +export function getAllNetworks() { + const config = loadContractConfig(); + return Object.keys(config.network).filter(key => key !== 'default'); +} + +export function getDefaultNetwork() { + const config = loadContractConfig(); + return config.network.default; +} diff --git a/scripts/lib/contract-config.test.js b/scripts/lib/contract-config.test.js index 51478758..278d191c 100644 --- a/scripts/lib/contract-config.test.js +++ b/scripts/lib/contract-config.test.js @@ -108,3 +108,43 @@ describe('contract-config', () => { }); }); }); + +describe('getLegacyContractName', () => { + it('returns legacy contract name for v1', () => { + const name = getLegacyContractName('1'); + expect(name).toBe('sprintfund-core'); + }); + + it('returns null for non-existent version', () => { + const name = getLegacyContractName('99'); + expect(name).toBeNull(); + }); +}); + +describe('isLegacyContract', () => { + it('returns true for legacy contract', () => { + const result = isLegacyContract('sprintfund-core'); + expect(result).toBe(true); + }); + + it('returns false for current contract', () => { + const result = isLegacyContract('sprintfund-core-v3'); + expect(result).toBe(false); + }); +}); + +describe('getAllNetworks', () => { + it('returns all network names', () => { + const networks = getAllNetworks(); + expect(networks).toContain('mainnet'); + expect(networks).toContain('testnet'); + expect(networks).not.toContain('default'); + }); +}); + +describe('getDefaultNetwork', () => { + it('returns the default network', () => { + const network = getDefaultNetwork(); + expect(network).toBe('mainnet'); + }); +}); diff --git a/scripts/lib/index.js b/scripts/lib/index.js index ee0c0912..88e42b85 100644 --- a/scripts/lib/index.js +++ b/scripts/lib/index.js @@ -1 +1 @@ -export { loadContractConfig, getContractAddress, getContractName, getContractPrincipal, getNetworkConfig, getContractVersion, getLegacyContractName, isLegacyContract } from './contract-config.js'; +export { loadContractConfig, getContractAddress, getContractName, getContractPrincipal, getNetworkConfig, getContractVersion, getLegacyContractName, isLegacyContract, getAllNetworks, getDefaultNetwork } from './contract-config.js'; diff --git a/scripts/package.json b/scripts/package.json index 8a5c8483..b7564059 100644 --- a/scripts/package.json +++ b/scripts/package.json @@ -8,6 +8,7 @@ "deploy-logger": "node deploy-logger.js", "call-logger": "node call-logger.js", "validate-config": "node scripts/validate-config.js", + "update-config": "node scripts/update-config.js", "test": "jest" }, "dependencies": {