tax rate, version and compose (#70)

resolves #67

Co-authored-by: johba <johba@harb.eth>
Reviewed-on: https://codeberg.org/johba/harb/pulls/70
This commit is contained in:
johba 2025-10-07 19:26:08 +02:00
parent d8ca557eb6
commit 6cbb1781ce
40 changed files with 1243 additions and 213 deletions

View file

@ -1,7 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../kraiken-lib"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$REPO_ROOT/kraiken-lib"
if [[ ! -d node_modules || ! -x node_modules/.bin/tsc ]]; then
if ! npm install --silent; then
@ -9,6 +12,9 @@ if [[ ! -d node_modules || ! -x node_modules/.bin/tsc ]]; then
fi
fi
# Ensure tax rate data mirrors onchain Stake.sol before compiling
node "$SCRIPT_DIR/sync-tax-rates.mjs"
./node_modules/.bin/tsc
echo "kraiken-lib built"

View file

@ -27,7 +27,64 @@ start_stack() {
./scripts/build-kraiken-lib.sh
echo "Starting stack..."
podman-compose up -d
# Start services in strict dependency order with explicit create+start
# This avoids podman dependency graph issues
# Create all containers first (without starting)
echo " Creating containers..."
podman-compose up --no-start 2>&1 | grep -v "STEP\|Copying\|Writing\|Getting\|fetch\|Installing\|Executing" || true
# Phase 1: Start base services (no dependencies)
echo " Starting anvil & postgres..."
podman-compose start anvil postgres >/dev/null 2>&1
# Wait for base services to be healthy
echo " Waiting for anvil & postgres..."
for i in {1..30}; do
anvil_healthy=$(podman healthcheck run harb_anvil_1 >/dev/null 2>&1 && echo "yes" || echo "no")
postgres_healthy=$(podman healthcheck run harb_postgres_1 >/dev/null 2>&1 && echo "yes" || echo "no")
if [[ "$anvil_healthy" == "yes" ]] && [[ "$postgres_healthy" == "yes" ]]; then
break
fi
sleep 2
done
# Phase 2: Start bootstrap (depends on anvil & postgres healthy)
echo " Starting bootstrap..."
podman-compose start bootstrap >/dev/null 2>&1
# Wait for bootstrap to complete
echo " Waiting for bootstrap..."
for i in {1..60}; do
bootstrap_status=$(podman inspect harb_bootstrap_1 --format='{{.State.Status}}')
if [[ "$bootstrap_status" == "exited" ]]; then
break
fi
sleep 2
done
# Phase 3: Start ponder (depends on bootstrap completed)
echo " Starting ponder..."
podman-compose start ponder >/dev/null 2>&1
# Wait for ponder to be healthy
echo " Waiting for ponder..."
for i in {1..60}; do
ponder_healthy=$(podman healthcheck run harb_ponder_1 >/dev/null 2>&1 && echo "yes" || echo "no")
if [[ "$ponder_healthy" == "yes" ]]; then
break
fi
sleep 2
done
# Phase 4: Start frontend services (depend on ponder healthy)
echo " Starting webapp, landing, txn-bot..."
podman-compose start webapp landing txn-bot >/dev/null 2>&1
# Phase 5: Start caddy (depends on frontend services)
sleep 5
echo " Starting caddy..."
podman-compose start caddy >/dev/null 2>&1
echo "Watching for kraiken-lib changes..."
./scripts/watch-kraiken-lib.sh &

106
scripts/sync-tax-rates.mjs Normal file
View file

@ -0,0 +1,106 @@
#!/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');