## Summary - Extract shared bootstrap functions into `scripts/bootstrap-common.sh` (eliminates ~120 lines of duplicated forge/cast commands from e2e.yml) - Create reusable `scripts/wait-for-service.sh` for health checks (replaces 60-line inline wait-for-stack) - Merge dev and CI entrypoints into unified scripts branching on `CI` env var (delete `docker/ci-entrypoints/`) - Replace 4 per-service CI Dockerfiles with parameterized `docker/Dockerfile.service-ci` - Add `sync-tax-rates.mjs` to CI image builder stage - Fix: CI now grants txnBot recenter access (was missing) - Fix: txnBot funding parameterized (CI=10eth, local=1eth) - Delete 5 obsolete migration docs and 4 DinD integration files Net: -1540 lines removed Closes #107 ## Test plan - [ ] E2E pipeline passes (bootstrap sources shared script, services use old images with commands override) - [ ] build-ci-images pipeline builds all 4 services with unified Dockerfile - [ ] Local dev stack boots via `./scripts/dev.sh start` 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: openhands <openhands@all-hands.dev> Reviewed-on: https://codeberg.org/johba/harb/pulls/108
8.4 KiB
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
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
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.tsfor all consumers
3. Ponder Validation (FAIL HARD)
File: services/ponder/src/helpers/version.ts
export async function validateContractVersion(context: Context): Promise<void> {
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
VERSIONfrom 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
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_VERSIONis loaded - Future enhancement: Query Ponder GraphQL for contract version
- Shows warning banner if mismatch (doesn't break app)
5. CI/CD Validation
File: .woodpecker/release.yml (version-check step)
The Woodpecker release pipeline validates version consistency on tagged releases. The version-check step:
- Builds kraiken-lib (including
sync-tax-rates.mjs) - Runs an inline Node.js script that:
- Extracts
VERSIONfromKraiken.sol - Extracts
KRAIKEN_LIB_VERSIONandCOMPATIBLE_CONTRACT_VERSIONSfromkraiken-lib/src/version.ts - Fails if contract VERSION differs from lib VERSION
- Fails if contract VERSION is not in COMPATIBLE_CONTRACT_VERSIONS
- Extracts
Triggered on: tag events (releases)
Prevents:
- Releasing with incompatible versions
- Deploying with stale kraiken-lib
Workflows
Version Bump (Breaking Change)
When modifying TAX_RATES or other critical data structures:
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:
// 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:
- Check if contract was upgraded
- Update
COMPATIBLE_CONTRACT_VERSIONSinkraiken-lib/src/version.ts - Run
./scripts/build-kraiken-lib.sh - Run
rm -rf services/ponder/.ponder/ - Restart Ponder
CI/CD check fails
❌ Contract VERSION (2) not in COMPATIBLE_CONTRACT_VERSIONS
Fix:
- Update
kraiken-lib/src/version.tsto 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
-
Store version in Ponder stats table
- Add
contractVersioncolumn tostats - Frontend can query via GraphQL
- Enables full 3-way validation (contract → Ponder → frontend)
- Add
-
Multi-contract versioning
- Add
VERSIONtoStake.sol,LiquidityManager.sol - Track all contract versions in kraiken-lib
- Validate full deployment is compatible
- Add
-
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_RATESarray - ✅ 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:
/**
* 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.