280 lines
8.4 KiB
Markdown
280 lines
8.4 KiB
Markdown
|
|
# 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<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 `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.
|