#!/usr/bin/env node /** * Sync TAX_RATE options from the on-chain Stake contract to kraiken-lib. * * The Stake.sol contract defines the canonical TAX_RATES array in basis points (yearly percentage * 100). * This script parses that array and regenerates kraiken-lib/src/taxRates.ts so that all front-end consumers * share a single source of truth that mirrors the deployed contract. */ import { readFileSync, writeFileSync } from 'node:fs'; import { fileURLToPath } from 'node:url'; import path from 'node:path'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const stakeSourcePath = path.resolve(__dirname, '../onchain/src/Stake.sol'); const taxRatesDestPath = path.resolve(__dirname, '../kraiken-lib/src/taxRates.ts'); const source = readFileSync(stakeSourcePath, 'utf8'); const match = source.match(/uint256\[]\s+public\s+TAX_RATES\s*=\s*\[([^\]]+)\]/m); if (!match) { throw new Error(`Unable to locate TAX_RATES array in ${stakeSourcePath}`); } const rawValues = match[1] .split(',') .map(value => value.trim()) .filter(Boolean); if (rawValues.length === 0) { throw new Error('TAX_RATES array is empty or failed to parse'); } const taxRateOptions = rawValues.map((value, index) => { const basisPoints = Number(value.replace(/_/g, '')); if (!Number.isFinite(basisPoints)) { throw new Error(`Invalid TAX_RATES entry "${value}" at index ${index}`); } const yearlyPercent = basisPoints; const decimalRate = yearlyPercent / 100; const dailyPercent = yearlyPercent / 365; return { index, year: yearlyPercent, daily: Number(dailyPercent.toFixed(5)), decimal: Number(decimalRate.toFixed(2)), }; }); // Generate checksum for runtime validation import { createHash } from 'node:crypto'; const taxRatesString = rawValues.join(','); const checksum = createHash('sha256').update(taxRatesString).digest('hex').slice(0, 16); const fileHeader = `/** * AUTO-GENERATED FILE — DO NOT EDIT * * Generated by scripts/sync-tax-rates.mjs from onchain/src/Stake.sol. * Run \`node scripts/sync-tax-rates.mjs\` after modifying the contract TAX_RATES array. */ `; const interfaceDef = `export interface TaxRateOption { index: number; year: number; daily: number; decimal: number; } `; const optionLines = taxRateOptions .map(option => ` { index: ${option.index}, year: ${option.year}, daily: ${option.daily}, decimal: ${option.decimal} }`) .join(',\n'); const content = `${fileHeader} ${interfaceDef} export const TAX_RATE_OPTIONS: TaxRateOption[] = [ ${optionLines} ]; /** * Checksum of the contract TAX_RATES array (first 16 chars of SHA-256). * Used for runtime validation to ensure kraiken-lib is in sync with deployed contracts. * * To validate at runtime: * 1. Read TAX_RATES array from the Stake contract * 2. Compute checksum: sha256(values.join(',')).slice(0, 16) * 3. Compare with TAX_RATES_CHECKSUM * * If mismatch: Run \`node scripts/sync-tax-rates.mjs\` and rebuild kraiken-lib. */ export const TAX_RATES_CHECKSUM = '${checksum}'; /** * Array of raw year values (matches contract uint256[] TAX_RATES exactly). * Used for runtime validation without needing to parse options. */ export const TAX_RATES_RAW = [${rawValues.join(', ')}]; `; writeFileSync(taxRatesDestPath, content.trimStart() + '\n');