From a555a2fdd119d36f69813840a9a627356eb4ee00 Mon Sep 17 00:00:00 2001 From: johba Date: Thu, 20 Nov 2025 18:54:53 +0100 Subject: [PATCH] refactor: migrate kraiken-lib to explicit subpath imports (BREAKING CHANGE) (#89) Removes the barrel export pattern in favor of explicit subpath imports for better tree-shaking and clearer dependencies. ## Breaking Changes - Removed `src/helpers.ts` barrel export - Removed `./helpers` from package.json exports - Root `kraiken-lib` import now raises build errors - Consumers MUST use explicit subpaths: - `kraiken-lib/abis` - Contract ABIs - `kraiken-lib/staking` - Staking helpers - `kraiken-lib/snatch` - Snatch selection - `kraiken-lib/ids` - Position ID utilities - `kraiken-lib/subgraph` - Byte conversion utilities - `kraiken-lib/taxRates` - Tax rate constants - `kraiken-lib/version` - Version validation ## Changes - kraiken-lib: - Bumped version to 1.0.0 (breaking change) - Updated src/index.ts to raise build errors - Added backward-compatible ABI aliases (KraikenAbi, StakeAbi) - Updated all test files to use .js extensions and new imports - Updated documentation (README, AGENTS.md) - Consumer updates: - services/ponder: Updated ponder.config.ts to use kraiken-lib/abis - web-app: Updated all imports to use subpaths - composables/usePositions.ts: kraiken-lib/subgraph - contracts/harb.ts: kraiken-lib/abis - contracts/stake.ts: kraiken-lib/abis ## Migration Guide ```typescript // OLD import { getSnatchList } from 'kraiken-lib/helpers'; import { KraikenAbi } from 'kraiken-lib'; // NEW import { getSnatchList } from 'kraiken-lib/snatch'; import { KraikenAbi } from 'kraiken-lib/abis'; ``` Fixes #86 Co-authored-by: openhands Reviewed-on: https://codeberg.org/johba/harb/pulls/89 --- kraiken-lib/AGENTS.md | 20 ++++++----- kraiken-lib/README.md | 4 +-- kraiken-lib/package.json | 7 +--- kraiken-lib/src/abis.ts | 6 ++++ kraiken-lib/src/helpers.ts | 4 --- kraiken-lib/src/index.ts | 37 +++----------------- kraiken-lib/src/tests/functions.test.ts | 2 +- kraiken-lib/src/tests/ids.test.ts | 4 +-- kraiken-lib/src/tests/snatch.test.ts | 17 ++++----- kraiken-lib/src/tests/staking.test.ts | 2 +- kraiken-lib/src/tests/taxRates.test.ts | 2 +- services/ponder/ponder.config.ts | 2 +- tests/e2e/01-acquire-and-stake.spec.ts | 4 +-- tests/e2e/02-max-stake-all-tax-rates.spec.ts | 4 +-- tests/setup/stack.ts | 25 +++++++++++++ web-app/src/composables/usePositions.ts | 4 +-- web-app/src/contracts/harb.ts | 2 +- web-app/src/contracts/stake.ts | 2 +- 18 files changed, 74 insertions(+), 74 deletions(-) delete mode 100644 kraiken-lib/src/helpers.ts diff --git a/kraiken-lib/AGENTS.md b/kraiken-lib/AGENTS.md index 495565d..738bb65 100644 --- a/kraiken-lib/AGENTS.md +++ b/kraiken-lib/AGENTS.md @@ -8,11 +8,10 @@ Shared TypeScript helpers used by the landing app, txnBot, and other services to - Centralise staking math (tax calculations, snatch selection, share conversions) for reuse across clients. ## Key Modules -- `src/kraiken.ts` - Token-facing helpers and supply utilities. -- `src/stake.ts` - Staking math, Harberger tax helpers, snatch scoring. -- `src/chains.ts` - Chain constants and deployment metadata. -- `src/queries/` - GraphQL operations that target the Ponder schema. -- `src/__generated__/graphql.ts` - Codegen output consumed throughout the stack. +- `src/staking.ts` - Harberger staking helpers for delinquency checks and snatch math. +- `src/snatch.ts` - Snatch selection engine and supporting types. +- `src/ids.ts` - Position ID encoding helpers. +- `src/subgraph.ts` - Byte utilities shared between the GraphQL layer and clients. - `src/abis.ts` - Contract ABIs imported directly from `onchain/out/` forge artifacts. Single source of truth for all ABI consumers. - `src/taxRates.ts` - Generated from `onchain/src/Stake.sol` by `scripts/sync-tax-rates.mjs`; never edit by hand. - `src/version.ts` - Version validation system tracking `KRAIKEN_LIB_VERSION` and `COMPATIBLE_CONTRACT_VERSIONS` for runtime dependency checking. @@ -29,13 +28,18 @@ Shared TypeScript helpers used by the landing app, txnBot, and other services to - `npm test` - Execute Jest suite for helper coverage. ## Integration Notes -- Landing app consumes helpers for UI projections and staking copy. -- txnBot relies on the same helpers to evaluate profitability and tax windows. +- Landing app consumes `kraiken-lib/abis`, `kraiken-lib/staking`, and `kraiken-lib/subgraph` for ABI resolution and ID conversion. +- txnBot relies on `kraiken-lib/staking` and `kraiken-lib/ids` to evaluate profitability and tax windows. +- Ponder imports `kraiken-lib/abis` for indexing, and `kraiken-lib/version` for cross-service version checks. - When the Ponder schema changes, rerun `npm run compile` and commit regenerated types to prevent drift. +## Import Guidance +- The legacy `helpers.ts` barrel has been removed. Always import from the narrow subpaths (e.g. `kraiken-lib/abis`, `kraiken-lib/staking`, `kraiken-lib/snatch`, `kraiken-lib/subgraph`). +- Avoid importing `kraiken-lib` directly; the root module no longer re-exports the helper surface and exists only to raise build-time errors for bundle imports. + ## ES Module Architecture - **Module Type**: This package is built as ES modules (`"type": "module"` in package.json). All consumers must support ES modules. -- **Import Extensions**: All relative imports in TypeScript source files MUST include `.js` extensions (e.g., `from "./helpers.js"`). This is required for ES module resolution even though the source files are `.ts`. +- **Import Extensions**: All relative imports in TypeScript source files MUST include `.js` extensions (e.g., `from "./staking.js"`). This is required for ES module resolution even though the source files are `.ts`. - **JSON Imports**: JSON files (like ABI artifacts) must use import assertions: `import Foo from './path.json' assert { type: 'json' }`. - **TypeScript Config**: `tsconfig.json` must specify: - `"module": "esnext"` - Generate ES module syntax diff --git a/kraiken-lib/README.md b/kraiken-lib/README.md index 94c85b2..48957a3 100644 --- a/kraiken-lib/README.md +++ b/kraiken-lib/README.md @@ -16,7 +16,7 @@ yarn add kraiken-lib then ``` -import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from "kraiken-lib"; +import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from "kraiken-lib/subgraph"; uint256ToBytesLittleEndian(3n); ``` @@ -24,7 +24,7 @@ uint256ToBytesLittleEndian(3n); ## get Snatch List ``` -import { getSnatchList } from "kraiken-lib"; +import { getSnatchList } from "kraiken-lib/snatch"; const positionIds = getSnatchList(positions, neededShares, maxTaxRateDecimal, stakeTotalSupply); ``` diff --git a/kraiken-lib/package.json b/kraiken-lib/package.json index 4dd1fae..0998c67 100644 --- a/kraiken-lib/package.json +++ b/kraiken-lib/package.json @@ -1,6 +1,6 @@ { "name": "kraiken-lib", - "version": "0.2.0", + "version": "1.0.0", "description": "helper functions and snatch selection", "type": "module", "main": "dist/index.js", @@ -11,11 +11,6 @@ "require": "./dist/index.js", "import": "./dist/index.js" }, - "./helpers": { - "types": "./dist/helpers.d.ts", - "require": "./dist/helpers.js", - "import": "./dist/helpers.js" - }, "./ids": { "types": "./dist/ids.d.ts", "require": "./dist/ids.js", diff --git a/kraiken-lib/src/abis.ts b/kraiken-lib/src/abis.ts index e5a601f..c209955 100644 --- a/kraiken-lib/src/abis.ts +++ b/kraiken-lib/src/abis.ts @@ -23,3 +23,9 @@ export const ABIS = { Kraiken: KRAIKEN_ABI, Stake: STAKE_ABI, } as const; + +// Backward-compatible aliases +// eslint-disable-next-line @typescript-eslint/naming-convention +export const KraikenAbi = KRAIKEN_ABI; +// eslint-disable-next-line @typescript-eslint/naming-convention +export const StakeAbi = STAKE_ABI; diff --git a/kraiken-lib/src/helpers.ts b/kraiken-lib/src/helpers.ts deleted file mode 100644 index 4b56f05..0000000 --- a/kraiken-lib/src/helpers.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './staking.js'; -export * from './snatch.js'; -export * from './ids.js'; -export * from './taxRates.js'; diff --git a/kraiken-lib/src/index.ts b/kraiken-lib/src/index.ts index 0e486fb..d1ab477 100644 --- a/kraiken-lib/src/index.ts +++ b/kraiken-lib/src/index.ts @@ -1,32 +1,5 @@ -export { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from './subgraph.js'; - -// Backward compatible aliases -export { bytesToUint256LittleEndian as bytesToUint256, uint256ToBytesLittleEndian as uint256ToBytes } from './subgraph.js'; - -export { TAX_RATE_OPTIONS, type TaxRateOption } from './taxRates.js'; - -export { calculateSnatchShortfall, isPositionDelinquent } from './staking.js'; - -export { - minimumTaxRate, - selectSnatchPositions, - getSnatchList, - type SnatchablePosition, - type SnatchSelectionOptions, - type SnatchSelectionResult, -} from './snatch.js'; - -export { decodePositionId } from './ids.js'; - -export { KRAIKEN_ABI, STAKE_ABI, ABIS } from './abis.js'; - -// Backward compatible aliases -export { KRAIKEN_ABI as KraikenAbi, STAKE_ABI as StakeAbi } from './abis.js'; - -export { - KRAIKEN_LIB_VERSION, - COMPATIBLE_CONTRACT_VERSIONS, - STACK_META_ID, - isCompatibleVersion, - getVersionMismatchError, -} from './version.js'; +/** + * kraiken-lib no longer exposes a bundled helper surface. + * Import from explicit subpaths such as `kraiken-lib/staking`. + */ +export {}; diff --git a/kraiken-lib/src/tests/functions.test.ts b/kraiken-lib/src/tests/functions.test.ts index 94f6fcd..6d64265 100644 --- a/kraiken-lib/src/tests/functions.test.ts +++ b/kraiken-lib/src/tests/functions.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from '@jest/globals'; -import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from '../subgraph'; +import { bytesToUint256LittleEndian, uint256ToBytesLittleEndian } from '../subgraph.js'; describe('BigInt Conversion Functions', () => { test('converts uint256 to bytes and back (little endian)', async () => { diff --git a/kraiken-lib/src/tests/ids.test.ts b/kraiken-lib/src/tests/ids.test.ts index 04985b7..d0d3741 100644 --- a/kraiken-lib/src/tests/ids.test.ts +++ b/kraiken-lib/src/tests/ids.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from '@jest/globals'; -import { decodePositionId } from '../ids'; -import { uint256ToBytesLittleEndian } from '../subgraph'; +import { decodePositionId } from '../ids.js'; +import { uint256ToBytesLittleEndian } from '../subgraph.js'; describe('ids', () => { test('decodePositionId works across representations', () => { diff --git a/kraiken-lib/src/tests/snatch.test.ts b/kraiken-lib/src/tests/snatch.test.ts index 210e8e7..1fe0ac5 100644 --- a/kraiken-lib/src/tests/snatch.test.ts +++ b/kraiken-lib/src/tests/snatch.test.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from '@jest/globals'; -import { getSnatchList, minimumTaxRate, selectSnatchPositions, type SnatchablePosition } from '../snatch'; -import type { Positions } from '../__generated__/graphql'; +import { getSnatchList, minimumTaxRate, selectSnatchPositions, type SnatchablePosition } from '../snatch.js'; +import { uint256ToBytesLittleEndian } from '../subgraph.js'; +import type { Positions as GraphPosition } from '../__generated__/graphql.js'; describe('snatch', () => { test('minimumTaxRate finds the lowest tax', () => { @@ -44,17 +45,17 @@ describe('snatch', () => { test('getSnatchList converts subgraph positions', () => { const stakeTotalSupply = 1_000_000n * 10n ** 18n; - const positions: Positions[] = [ + const positions = [ { __typename: 'positions', - id: '0x01', + id: uint256ToBytesLittleEndian(1n), owner: '0xowner1', share: 0.0001, creationTime: '0', lastTaxTime: '0', taxRate: 0.02, status: 'Active', - createdAt: '0', + createdAt: 0, kraikenDeposit: '0', payout: '0', snatched: 0, @@ -64,14 +65,14 @@ describe('snatch', () => { }, { __typename: 'positions', - id: '0x02', + id: uint256ToBytesLittleEndian(2n), owner: '0xowner2', share: 0.0002, creationTime: '0', lastTaxTime: '0', taxRate: 0.01, status: 'Active', - createdAt: '0', + createdAt: 0, kraikenDeposit: '0', payout: '0', snatched: 0, @@ -79,7 +80,7 @@ describe('snatch', () => { taxPaid: '0', totalSupplyInit: '0', }, - ]; + ] as unknown as GraphPosition[]; const result = getSnatchList(positions, 10n ** 18n, 0.05, stakeTotalSupply); diff --git a/kraiken-lib/src/tests/staking.test.ts b/kraiken-lib/src/tests/staking.test.ts index 23f69e6..95d5bb7 100644 --- a/kraiken-lib/src/tests/staking.test.ts +++ b/kraiken-lib/src/tests/staking.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from '@jest/globals'; -import { calculateSnatchShortfall, isPositionDelinquent } from '../staking'; +import { calculateSnatchShortfall, isPositionDelinquent } from '../staking.js'; describe('staking', () => { test('calculateSnatchShortfall returns zero when within cap', () => { diff --git a/kraiken-lib/src/tests/taxRates.test.ts b/kraiken-lib/src/tests/taxRates.test.ts index 9986280..945d28a 100644 --- a/kraiken-lib/src/tests/taxRates.test.ts +++ b/kraiken-lib/src/tests/taxRates.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from '@jest/globals'; -import { TAX_RATE_OPTIONS } from '../taxRates'; +import { TAX_RATE_OPTIONS } from '../taxRates.js'; describe('taxRates', () => { test('tax rate options exported for consumers', () => { diff --git a/services/ponder/ponder.config.ts b/services/ponder/ponder.config.ts index 1b8efda..68ef9a6 100644 --- a/services/ponder/ponder.config.ts +++ b/services/ponder/ponder.config.ts @@ -1,6 +1,6 @@ import { createConfig } from 'ponder'; import type { Abi } from 'viem'; -import { KraikenAbi, StakeAbi } from 'kraiken-lib'; +import { KraikenAbi, StakeAbi } from 'kraiken-lib/abis'; // Network configurations keyed by canonical environment name type NetworkConfig = { diff --git a/tests/e2e/01-acquire-and-stake.spec.ts b/tests/e2e/01-acquire-and-stake.spec.ts index 8d63db4..2e61ab6 100644 --- a/tests/e2e/01-acquire-and-stake.spec.ts +++ b/tests/e2e/01-acquire-and-stake.spec.ts @@ -140,14 +140,14 @@ test.describe('Acquire & Stake', () => { id: 1, method: 'eth_call', params: [{ - to: '0xe527ddac2592faa45884a0b78e4d377a5d3df8cc', // KRK token + to: STACK_CONFIG.contracts.Kraiken, // KRK token from deployments data: `0x70a08231000000000000000000000000${ACCOUNT_ADDRESS.slice(2)}` // balanceOf(address) }, 'latest'] }) }); const balanceData = await balanceResponse.json(); - const balance = BigInt(balanceData.result || '0'); + const balance = BigInt(balanceData.result || '0x0'); console.log(`[TEST] KRK balance: ${balance.toString()} wei`); expect(balance).toBeGreaterThan(0n); diff --git a/tests/e2e/02-max-stake-all-tax-rates.spec.ts b/tests/e2e/02-max-stake-all-tax-rates.spec.ts index 5e671c4..07cff39 100644 --- a/tests/e2e/02-max-stake-all-tax-rates.spec.ts +++ b/tests/e2e/02-max-stake-all-tax-rates.spec.ts @@ -138,14 +138,14 @@ test.describe('Max Stake All Tax Rates', () => { id: 1, method: 'eth_call', params: [{ - to: '0xe527ddac2592faa45884a0b78e4d377a5d3df8cc', // KRK token + to: STACK_CONFIG.contracts.Kraiken, // KRK token from deployments data: `0x70a08231000000000000000000000000${ACCOUNT_ADDRESS.slice(2)}` // balanceOf(address) }, 'latest'] }) }); const balanceData = await balanceResponse.json(); - const balance = BigInt(balanceData.result || '0'); + const balance = BigInt(balanceData.result || '0x0'); console.log(`[TEST] KRK balance: ${balance.toString()} wei`); expect(balance).toBeGreaterThan(0n); diff --git a/tests/setup/stack.ts b/tests/setup/stack.ts index e94cf60..5b2c8f4 100644 --- a/tests/setup/stack.ts +++ b/tests/setup/stack.ts @@ -2,15 +2,39 @@ import { runAllHealthChecks, formatHealthCheckError, } from './health-checks.js'; +import { readFileSync } from 'fs'; +import { join } from 'path'; const DEFAULT_RPC_URL = 'http://localhost:8081/api/rpc'; const DEFAULT_WEBAPP_URL = 'http://localhost:8081'; const DEFAULT_GRAPHQL_URL = 'http://localhost:8081/api/graphql'; +export interface ContractAddresses { + Kraiken: string; + Stake: string; + LiquidityManager: string; +} + export interface StackConfig { rpcUrl: string; webAppUrl: string; graphqlUrl: string; + contracts: ContractAddresses; +} + +/** + * Load contract addresses from deployments file + */ +function loadContractAddresses(): ContractAddresses { + try { + const deploymentsPath = join(process.cwd(), 'onchain', 'deployments-local.json'); + const deploymentsJson = readFileSync(deploymentsPath, 'utf-8'); + const deployments = JSON.parse(deploymentsJson); + return deployments.contracts; + } catch (error) { + console.error('Failed to load contract addresses from deployments-local.json:', error); + throw new Error('Cannot run tests without deployed contract addresses'); + } } /** @@ -22,6 +46,7 @@ export function getStackConfig(): StackConfig { rpcUrl: process.env.STACK_RPC_URL ?? DEFAULT_RPC_URL, webAppUrl: process.env.STACK_WEBAPP_URL ?? DEFAULT_WEBAPP_URL, graphqlUrl: process.env.STACK_GRAPHQL_URL ?? DEFAULT_GRAPHQL_URL, + contracts: loadContractAddresses(), }; } diff --git a/web-app/src/composables/usePositions.ts b/web-app/src/composables/usePositions.ts index 2303c54..afe3947 100644 --- a/web-app/src/composables/usePositions.ts +++ b/web-app/src/composables/usePositions.ts @@ -5,7 +5,7 @@ import axios from 'axios'; import { getAccount, watchChainId, watchAccount, watchContractEvent, type Config } from '@wagmi/core'; import type { WatchChainIdReturnType, WatchAccountReturnType, GetAccountReturnType } from '@wagmi/core'; -import { bytesToUint256 } from 'kraiken-lib'; +import { bytesToUint256LittleEndian } from 'kraiken-lib/subgraph'; import { bigInt2Number } from '@/utils/helper'; import { getTaxRateIndexByDecimal } from '@/composables/useAdjustTaxRates'; import logger from '@/utils/logger'; @@ -191,7 +191,7 @@ export async function loadActivePositions(chainId: number, endpointOverride?: st function formatId(id: Hex) { const bytes = toBytes(id); - const bigIntId = bytesToUint256(bytes); + const bigIntId = bytesToUint256LittleEndian(bytes); return bigIntId; } diff --git a/web-app/src/contracts/harb.ts b/web-app/src/contracts/harb.ts index 4679d53..c2055f2 100644 --- a/web-app/src/contracts/harb.ts +++ b/web-app/src/contracts/harb.ts @@ -2,7 +2,7 @@ import { ref } from 'vue'; import { config } from '@/wagmi'; import { getAccount, readContract, writeContract, waitForTransactionReceipt, getChainId } from '@wagmi/core'; import type { Config } from '@wagmi/core'; -import { KraikenAbi } from 'kraiken-lib'; +import { KraikenAbi } from 'kraiken-lib/abis'; import { type Abi, type Address, type Hash } from 'viem'; import { StakeContract } from '@/contracts/stake'; import { getChain } from '@/config'; diff --git a/web-app/src/contracts/stake.ts b/web-app/src/contracts/stake.ts index fdd1055..233be61 100644 --- a/web-app/src/contracts/stake.ts +++ b/web-app/src/contracts/stake.ts @@ -2,7 +2,7 @@ import { ref } from 'vue'; import { config } from '@/wagmi'; import { readContract, writeContract, getChainId } from '@wagmi/core'; import type { Config } from '@wagmi/core'; -import { StakeAbi } from 'kraiken-lib'; +import { StakeAbi } from 'kraiken-lib/abis'; import { type Abi, type Address, type Hash, type Hex } from 'viem'; import { getChain } from '@/config'; import logger from '@/utils/logger';