# Version Validation System ## Overview The Kraiken protocol now includes a **version validation system** that ensures all stack components (contract, indexer, frontend) are synchronized and compatible. This prevents subtle data corruption bugs that arise from version mismatches. ## Architecture ``` ┌─────────────────────────────────────┐ │ Kraiken.sol │ │ uint256 public constant VERSION=1 │ ← Source of Truth └──────────────┬──────────────────────┘ │ read at startup ▼ ┌─────────────────────────────────────┐ │ Ponder Indexer │ │ • Reads contract.VERSION() │ │ • Validates against │ │ COMPATIBLE_CONTRACT_VERSIONS │ │ • FAILS HARD if mismatch │ └──────────────┬──────────────────────┘ │ GraphQL ▼ ┌─────────────────────────────────────┐ │ Frontend (web-app) │ │ • Checks KRAIKEN_LIB_VERSION │ │ • Shows warning if issues │ │ • Continues operation │ └─────────────────────────────────────┘ ``` ## Components ### 1. Contract Version Constant **File:** `onchain/src/Kraiken.sol` ```solidity contract Kraiken is ERC20, ERC20Permit { /** * @notice Protocol version for data structure compatibility. * Increment when making breaking changes to TAX_RATES, events, or core data structures. */ uint256 public constant VERSION = 1; // ... } ``` **Key properties:** - `public constant` = free to read, immutable - Forces contract redeployment on breaking changes - Single source of truth for the entire stack ### 2. kraiken-lib Version Tracking **File:** `kraiken-lib/src/version.ts` ```typescript export const KRAIKEN_LIB_VERSION = 1; export const COMPATIBLE_CONTRACT_VERSIONS = [1]; export function isCompatibleVersion(contractVersion: number): boolean { return COMPATIBLE_CONTRACT_VERSIONS.includes(contractVersion); } ``` **Key features:** - Manually maintained list (not parsed from contract) - Allows backward compatibility (lib can support multiple contract versions) - Exported by `kraiken-lib/src/index.ts` for all consumers ### 3. Ponder Validation (FAIL HARD) **File:** `services/ponder/src/helpers/version.ts` ```typescript export async function validateContractVersion(context: Context): Promise { const contractVersion = await context.client.readContract({ address: context.contracts.Kraiken.address, abi: context.contracts.Kraiken.abi, functionName: 'VERSION', }); if (!isCompatibleVersion(Number(contractVersion))) { console.error(getVersionMismatchError(contractVersion, 'ponder')); process.exit(1); // FAIL HARD } } ``` **Called in:** `services/ponder/src/kraiken.ts` (first Transfer event) **Behavior:** - Reads `VERSION` from deployed Kraiken contract - Compares against `COMPATIBLE_CONTRACT_VERSIONS` - **Exits with error** if incompatible (prevents indexing wrong data) - Logs success if compatible ### 4. Frontend Validation (WARN) **File:** `web-app/src/composables/useVersionCheck.ts` ```typescript export function useVersionCheck() { async function checkVersions(graphqlUrl: string) { // Currently placeholder - validates lib loaded correctly // Future: Query Ponder for stored contract version versionStatus.value = { isValid: true, libVersion: KRAIKEN_LIB_VERSION, }; } // ... } ``` **Behavior:** - Validates `KRAIKEN_LIB_VERSION` is loaded - Future enhancement: Query Ponder GraphQL for contract version - Shows warning banner if mismatch (doesn't break app) ### 5. CI/CD Validation **File:** `.github/workflows/validate-version.yml` ```yaml - name: Extract versions and validate run: | CONTRACT_VERSION=$(grep -oP 'VERSION\s*=\s*\K\d+' onchain/src/Kraiken.sol) LIB_VERSION=$(grep -oP 'KRAIKEN_LIB_VERSION\s*=\s*\K\d+' kraiken-lib/src/version.ts) COMPATIBLE=$(grep -oP 'COMPATIBLE_CONTRACT_VERSIONS\s*=\s*\[\K[^\]]+' kraiken-lib/src/version.ts) if echo ",$COMPATIBLE," | grep -q ",$CONTRACT_VERSION,"; then echo "✓ Version sync validated" else exit 1 fi ``` **Triggered on:** - PRs touching `Kraiken.sol` or `version.ts` - Pushes to `master`/`main` **Prevents:** - Merging incompatible versions - Deploying with stale kraiken-lib ## Workflows ### Version Bump (Breaking Change) When modifying TAX_RATES or other critical data structures: ```bash 1. Edit onchain/src/Kraiken.sol uint256 public constant VERSION = 2; // Increment 2. Edit kraiken-lib/src/version.ts export const KRAIKEN_LIB_VERSION = 2; export const COMPATIBLE_CONTRACT_VERSIONS = [2]; // Or [1, 2] for backward compat 3. Rebuild kraiken-lib ./scripts/build-kraiken-lib.sh 4. Clear Ponder state rm -rf services/ponder/.ponder/ 5. Redeploy contracts (if needed) cd onchain && forge script ... 6. Restart stack ./scripts/dev.sh restart --full ``` ### Adding Backward Compatibility If lib can work with old AND new contract versions: ```typescript // kraiken-lib/src/version.ts export const KRAIKEN_LIB_VERSION = 2; export const COMPATIBLE_CONTRACT_VERSIONS = [1, 2]; // Supports both ``` Ponder will accept either v1 or v2 contracts. ### Troubleshooting #### Ponder fails with "VERSION MISMATCH" ``` ╔════════════════════════════════════════════════════════════╗ ║ ❌ CRITICAL: VERSION MISMATCH (ponder) ╠════════════════════════════════════════════════════════════╣ ║ Contract VERSION: 2 ║ Library VERSION: 1 ║ Compatible versions: 1 ``` **Fix:** 1. Check if contract was upgraded 2. Update `COMPATIBLE_CONTRACT_VERSIONS` in `kraiken-lib/src/version.ts` 3. Run `./scripts/build-kraiken-lib.sh` 4. Run `rm -rf services/ponder/.ponder/` 5. Restart Ponder #### CI/CD check fails ``` ❌ Contract VERSION (2) not in COMPATIBLE_CONTRACT_VERSIONS ``` **Fix:** - Update `kraiken-lib/src/version.ts` to include new version - Or revert contract changes if unintentional ## Benefits ✅ **Prevents data corruption** - Ponder won't index with wrong schema ✅ **Early detection** - Fails at startup, not after hours of indexing ✅ **Clear errors** - Tells you exactly what to do ✅ **Backward compat** - Can support multiple versions if needed ✅ **CI enforcement** - Prevents merging incompatible changes ✅ **Future-proof** - Easy to extend to other contracts (Stake, LiquidityManager) ## Future Enhancements 1. **Store version in Ponder stats table** - Add `contractVersion` column to `stats` - Frontend can query via GraphQL - Enables full 3-way validation (contract → Ponder → frontend) 2. **Multi-contract versioning** - Add `VERSION` to `Stake.sol`, `LiquidityManager.sol` - Track all contract versions in kraiken-lib - Validate full deployment is compatible 3. **Version migration helpers** - Scripts to handle data migrations - Backward-compatible event handling - Gradual rollout support ## Maintenance ### When to increment VERSION Increment when changes affect **indexed data or frontend interpretation**: - ✅ Modifying `TAX_RATES` array - ✅ Adding/removing event parameters - ✅ Changing event names - ✅ Modifying struct definitions that frontends consume - ✅ Breaking changes to staking logic **Don't** increment for: - ❌ Gas optimizations (no data changes) - ❌ Internal logic changes (if events unchanged) - ❌ Comment updates - ❌ Pure view function changes ### Documentation Update `VERSION` comment in contract with each change: ```solidity /** * Version History: * - v1: Initial deployment (30-tier TAX_RATES) * - v2: Added new tax tier at 150% (31 tiers total) * - v3: Modified PositionCreated event (added taxRateIndex field) */ uint256 public constant VERSION = 3; ``` This creates an audit trail for version changes.