diff --git a/.gitignore b/.gitignore index 68ec8bc..63ca29e 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ ponder-repo tmp foundry.lock services/ponder/.env.local +node_modules diff --git a/AGENTS.md b/AGENTS.md index 5d65031..18911f4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,8 @@ - Fund the LiquidityManager with Base WETH (`0x4200...0006`) before expecting `recenter()` to succeed. - Ponder stores data in `.ponder/`; drop the directory if schema changes break migrations. - Keep git clean before committing; never leave commented-out code or untested changes. +- **ES Modules**: The entire stack uses ES modules. kraiken-lib, txnBot, Ponder, and web-app all require `"type": "module"` in package.json and use `import` syntax. +- **Podman Named Volumes**: `kraiken-lib/dist/` is mounted as a named volume (`harb_kraiken-dist`). After changing module format or build configuration, delete the volume with `podman volume rm harb_kraiken-dist` and restart to force a clean rebuild. ## Handy Commands - `foundryup` - update Foundry toolchain. diff --git a/containers/anvil-entrypoint.sh b/containers/anvil-entrypoint.sh index 8a1a541..fe69c78 100755 --- a/containers/anvil-entrypoint.sh +++ b/containers/anvil-entrypoint.sh @@ -2,7 +2,7 @@ set -euo pipefail MNEMONIC_FILE=/workspace/onchain/.secret.local -ANVIL_CMD=(anvil --fork-url "${FORK_URL:-https://sepolia.base.org}" --chain-id 31337 --block-time 1 --host 0.0.0.0 --port 8545) +ANVIL_CMD=(anvil --fork-url "${FORK_URL:-https://sepolia.base.org}" --chain-id 31337 --block-time 1 --host 0.0.0.0 --port 8545 --threads 4 --timeout 2000 --retries 2 --fork-retry-backoff 100) if [[ -f "$MNEMONIC_FILE" ]]; then MNEMONIC="$(tr -d '\n\r' <"$MNEMONIC_FILE")" diff --git a/containers/bootstrap.sh b/containers/bootstrap.sh index 1f035b6..680d0c5 100755 --- a/containers/bootstrap.sh +++ b/containers/bootstrap.sh @@ -1,6 +1,11 @@ #!/usr/bin/env bash set -euo pipefail +# Install jq if not available +if ! command -v jq >/dev/null 2>&1; then + apk add --no-cache jq >/dev/null 2>&1 || apt-get update && apt-get install -y jq >/dev/null 2>&1 || true +fi + ROOT_DIR=/workspace STATE_DIR=$ROOT_DIR/tmp/podman LOG_DIR=$STATE_DIR/logs @@ -35,9 +40,7 @@ log() { wait_for_rpc() { for _ in {1..120}; do - if curl -s -o /dev/null -X POST "$ANVIL_RPC" \ - -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","id":1,"method":"eth_chainId","params":[]}'; then + if cast chain-id --rpc-url "$ANVIL_RPC" >/dev/null 2>&1; then return 0 fi sleep 1 @@ -141,7 +144,7 @@ seed_application_state() { prime_chain() { log "Pre-mining blocks" - for _ in {1..1200}; do + for _ in {1..2000}; do cast rpc --rpc-url "$ANVIL_RPC" evm_mine >/dev/null 2>&1 || true done } @@ -153,6 +156,8 @@ KRAIKEN_ADDRESS=$KRAIKEN STAKE_ADDRESS=$STAKE START_BLOCK=$DEPLOY_BLOCK PONDER_RPC_URL_BASE_SEPOLIA_LOCAL_FORK=$ANVIL_RPC +DATABASE_URL=postgresql://ponder:ponder_local@postgres:5432/ponder_local +DATABASE_SCHEMA=ponder_local_${DEPLOY_BLOCK} EOPONDER } diff --git a/containers/ponder-dev-entrypoint.sh b/containers/ponder-dev-entrypoint.sh index 7ecfffb..73dd0ce 100755 --- a/containers/ponder-dev-entrypoint.sh +++ b/containers/ponder-dev-entrypoint.sh @@ -11,12 +11,21 @@ while [[ ! -f "$CONTRACT_ENV" ]]; do done cd "$PONDER_WORKDIR" + echo "[ponder-entrypoint] Installing dependencies..." npm install --no-save --loglevel error 2>&1 || { echo "[ponder-entrypoint] npm install failed, trying with --force" npm install --force --no-save --loglevel error } +# Load and export all environment variables from .env.local +if [[ -f .env.local ]]; then + echo "[ponder-entrypoint] Loading environment from .env.local" + set -a + source .env.local + set +a +fi + export CHOKIDAR_USEPOLLING=${CHOKIDAR_USEPOLLING:-1} export HOST=0.0.0.0 export PORT=${PORT:-42069} diff --git a/containers/txn-bot-entrypoint.sh b/containers/txn-bot-entrypoint.sh index 9734aea..ea51568 100755 --- a/containers/txn-bot-entrypoint.sh +++ b/containers/txn-bot-entrypoint.sh @@ -31,16 +31,7 @@ fi echo "[txn-bot-entrypoint] Compiling TypeScript..." if [[ ! -f dist/index.js ]]; then echo "[txn-bot-entrypoint] Running tsc to compile kraiken-lib..." - ./node_modules/.bin/tsc \ - --outDir dist \ - --declaration \ - --module commonjs \ - --target es2020 \ - --skipLibCheck \ - --esModuleInterop \ - --allowSyntheticDefaultImports \ - --resolveJsonModule \ - src/index.ts src/helpers.ts src/subgraph.ts src/__generated__/graphql.ts 2>&1 | head -20 || { + ./node_modules/.bin/tsc 2>&1 | head -20 || { echo "[txn-bot-entrypoint] TypeScript compilation had some issues, checking if files were created..." } diff --git a/kraiken-lib/AGENTS.md b/kraiken-lib/AGENTS.md index 0382a5d..c9190eb 100644 --- a/kraiken-lib/AGENTS.md +++ b/kraiken-lib/AGENTS.md @@ -13,6 +13,7 @@ Shared TypeScript helpers used by the landing app, txnBot, and other services to - `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/abis.ts` - Contract ABIs imported directly from `onchain/out/` forge artifacts. Single source of truth for all ABI consumers. ## GraphQL Code Generation - Schema source points to the Ponder GraphQL endpoint for the active environment. @@ -30,6 +31,17 @@ Shared TypeScript helpers used by the landing app, txnBot, and other services to - txnBot relies on the same helpers to evaluate profitability and tax windows. - When the Ponder schema changes, rerun `npm run compile` and commit regenerated types to prevent drift. +## 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`. +- **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 + - `"moduleResolution": "node"` - Enable proper module resolution + - `"rootDir": "./src"` - Ensure flat output structure in `dist/` +- **Build Output**: Running `npx tsc` produces ES module `.js` files in `dist/` that can be consumed by both browser (Vite) and Node.js (≥14 with `"type": "module"`). +- **Container Volumes**: In Podman/Docker setups, `dist/` is often a named volume. After changing module format or imports, delete the volume (`podman volume rm harb_kraiken-dist`) before restarting to force a clean rebuild. + ## Quality Guidelines - Keep helpers pure and side-effect free; they should accept explicit dependencies. - Prefer narrow exports so downstream consumers can tree-shake bundles. diff --git a/kraiken-lib/package-lock.json b/kraiken-lib/package-lock.json index 3236d84..fe3ce33 100644 --- a/kraiken-lib/package-lock.json +++ b/kraiken-lib/package-lock.json @@ -213,6 +213,7 @@ "integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", @@ -2893,6 +2894,7 @@ "integrity": "sha512-F1CBxgqwOMc4GKJ7eY22hWhBVQuMYTtqI8L0FcszYcpYX0fzfDGpez22Xau8Mgm7O9fI+zA/TYIdq3tGWfweBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.13.0" } @@ -3402,6 +3404,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001587", "electron-to-chromium": "^1.4.668", @@ -4617,6 +4620,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -4701,6 +4705,7 @@ "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", "devOptional": true, "license": "MIT", + "peer": true, "workspaces": [ "website" ], @@ -5344,6 +5349,7 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -7862,6 +7868,7 @@ "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8154,6 +8161,7 @@ "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/kraiken-lib/package.json b/kraiken-lib/package.json index 3949370..1f7b799 100644 --- a/kraiken-lib/package.json +++ b/kraiken-lib/package.json @@ -2,8 +2,15 @@ "name": "kraiken-lib", "version": "0.2.0", "description": "helper functions and snatch selection", + "type": "module", "main": "dist/index.js", "types": "dist/index.d.ts", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, "files": [ "/dist" ], diff --git a/kraiken-lib/src/abis.ts b/kraiken-lib/src/abis.ts index 785ec79..04667e0 100644 --- a/kraiken-lib/src/abis.ts +++ b/kraiken-lib/src/abis.ts @@ -5,8 +5,8 @@ * To update: run `forge build` in onchain/ directory */ -const KraikenForgeOutput = require('../../onchain/out/Kraiken.sol/Kraiken.json'); -const StakeForgeOutput = require('../../onchain/out/Stake.sol/Stake.json'); +import KraikenForgeOutput from '../../onchain/out/Kraiken.sol/Kraiken.json' assert { type: 'json' }; +import StakeForgeOutput from '../../onchain/out/Stake.sol/Stake.json' assert { type: 'json' }; /** * Kraiken ERC20 token contract ABI diff --git a/kraiken-lib/src/helpers.ts b/kraiken-lib/src/helpers.ts index 4f76fbb..2a50d6c 100644 --- a/kraiken-lib/src/helpers.ts +++ b/kraiken-lib/src/helpers.ts @@ -1,4 +1,4 @@ -export * from "./staking"; -export * from "./snatch"; -export * from "./ids"; -export * from "./taxRates"; +export * from "./staking.js"; +export * from "./snatch.js"; +export * from "./ids.js"; +export * from "./taxRates.js"; diff --git a/kraiken-lib/src/ids.ts b/kraiken-lib/src/ids.ts index 09cd654..4be0170 100644 --- a/kraiken-lib/src/ids.ts +++ b/kraiken-lib/src/ids.ts @@ -1,4 +1,4 @@ -import { bytesToUint256LittleEndian } from "./subgraph"; +import { bytesToUint256LittleEndian } from "./subgraph.js"; export function toBigIntId(id: string | Uint8Array | number | bigint): bigint { if (typeof id === "bigint") return id; diff --git a/kraiken-lib/src/index.ts b/kraiken-lib/src/index.ts index d165b82..8e0fc01 100644 --- a/kraiken-lib/src/index.ts +++ b/kraiken-lib/src/index.ts @@ -1,20 +1,20 @@ export { bytesToUint256LittleEndian, uint256ToBytesLittleEndian, -} from "./subgraph"; +} from "./subgraph.js"; // Backward compatible aliases export { bytesToUint256LittleEndian as bytesToUint256, uint256ToBytesLittleEndian as uint256ToBytes, -} from "./subgraph"; +} from "./subgraph.js"; -export { TAX_RATE_OPTIONS, type TaxRateOption } from "./taxRates"; +export { TAX_RATE_OPTIONS, type TaxRateOption } from "./taxRates.js"; export { calculateSnatchShortfall, isPositionDelinquent, -} from "./staking"; +} from "./staking.js"; export { minimumTaxRate, @@ -23,8 +23,8 @@ export { type SnatchablePosition, type SnatchSelectionOptions, type SnatchSelectionResult, -} from "./snatch"; +} from "./snatch.js"; -export { decodePositionId } from "./ids"; +export { decodePositionId } from "./ids.js"; -export { KraikenAbi, StakeAbi, ABIS } from "./abis"; +export { KraikenAbi, StakeAbi, ABIS } from "./abis.js"; diff --git a/kraiken-lib/src/snatch.ts b/kraiken-lib/src/snatch.ts index 9266df5..e398131 100644 --- a/kraiken-lib/src/snatch.ts +++ b/kraiken-lib/src/snatch.ts @@ -1,5 +1,5 @@ -import type { Position } from "./__generated__/graphql"; -import { toBigIntId } from "./ids"; +import type { Position } from "./__generated__/graphql.js"; +import { toBigIntId } from "./ids.js"; export interface SnatchablePosition { id: bigint; diff --git a/kraiken-lib/tsconfig.json b/kraiken-lib/tsconfig.json index b6930b9..267d004 100644 --- a/kraiken-lib/tsconfig.json +++ b/kraiken-lib/tsconfig.json @@ -1,12 +1,14 @@ { "compilerOptions": { - "module": "commonjs", + "module": "esnext", "target": "es2020", "declaration": true, "outDir": "./dist", + "rootDir": "./src", "skipLibCheck": true, "resolveJsonModule": true, "esModuleInterop": true, + "moduleResolution": "node", "types": ["node"] }, "include": [ diff --git a/onchain/AGENTS.md b/onchain/AGENTS.md index 5b3d156..7bf1942 100644 --- a/onchain/AGENTS.md +++ b/onchain/AGENTS.md @@ -10,9 +10,16 @@ ## Development Workflow - Tooling: Foundry (`forge build`, `forge test`, `forge fmt`, `forge snapshot`), Anvil for local chain, Base Sepolia deployment script (`forge script ...BaseSepoliaDeploy`). - Repo structure highlights: `src/` (core contracts), `test/helpers/` (Uniswap/Kraiken bases), `lib/uni-v3-lib` (math + JS setup), `script/` (deploy), `out/` (artifacts), config via `foundry.toml` & `remappings.txt`. -- Setup steps: clone repo, init/update submodules, install `lib/uni-v3-lib` dependencies (yarn), ensure Foundry installed. +- Setup steps: clone repo, init/update submodules (`git submodule update --init --recursive`), install `lib/uni-v3-lib` dependencies (`npm install` in `lib/uni-v3-lib/`), ensure Foundry installed. - **ABI Architecture**: Contract ABIs are exported via `kraiken-lib/src/abis.ts`, which imports directly from `onchain/out/` (forge build artifacts). All consumers (ponder, web-app) import from kraiken-lib for type-safe, single-source-of-truth ABIs. Run `forge build` in `onchain/` to update ABIs across the stack. +## Containerized Builds (Podman/Docker) +- **Git Submodules**: Must be initialized before building (`git submodule update --init --recursive`). Empty `lib/` directories cause compilation failures. +- **uni-v3-lib Dependencies**: Requires `npm install` in `lib/uni-v3-lib/` to populate `node_modules/` with Uniswap interfaces (IUniswapV3Pool, IUniswapV3Factory) and solady dependencies. +- **Foundry Image**: Use `ghcr.io/foundry-rs/foundry:latest` for containers. The image includes `forge`, `cast`, `anvil` but NOT `jq` or `curl`. +- **Root Access**: Bootstrap scripts that create deployment artifacts may need to run as root (user: "0:0") to write to mounted volumes. +- **Volume Permissions**: Use `:z` (shared SELinux label) instead of `:Z` (private) for multi-container access to the same mount. + ## Strategy & Mechanics - Outstanding supply excludes liquidity position balances; enforce 20% staking cap (~20k positions). - Anchor width and discovery depth adjusted dynamically; anchorShare tunes ETH allocation, discoveryDepth controls liquidity density multiples (≈2x-10x), capitalInefficiency shifts VWAP floor valuation (70%-170%). diff --git a/package-lock.json b/package-lock.json index 8a3b379..482d053 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -40,7 +39,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -58,7 +56,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -76,7 +73,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -94,7 +90,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -112,7 +107,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -130,7 +124,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -148,7 +141,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -166,7 +158,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -184,7 +175,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -202,7 +192,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -220,7 +209,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -238,7 +226,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -256,7 +243,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -274,7 +260,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -292,7 +277,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -310,7 +294,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -328,7 +311,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -346,7 +328,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -364,7 +345,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -382,7 +362,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -400,7 +379,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -418,7 +396,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -436,7 +413,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -454,7 +430,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -472,7 +447,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -1263,8 +1237,7 @@ "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-android-arm64": { "version": "4.52.2", @@ -1278,8 +1251,7 @@ "optional": true, "os": [ "android" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-darwin-arm64": { "version": "4.52.2", @@ -1293,8 +1265,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-darwin-x64": { "version": "4.52.2", @@ -1308,8 +1279,7 @@ "optional": true, "os": [ "darwin" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-freebsd-arm64": { "version": "4.52.2", @@ -1323,8 +1293,7 @@ "optional": true, "os": [ "freebsd" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-freebsd-x64": { "version": "4.52.2", @@ -1338,8 +1307,7 @@ "optional": true, "os": [ "freebsd" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { "version": "4.52.2", @@ -1353,8 +1321,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { "version": "4.52.2", @@ -1368,8 +1335,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { "version": "4.52.2", @@ -1383,8 +1349,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { "version": "4.52.2", @@ -1398,8 +1363,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { "version": "4.52.2", @@ -1413,8 +1377,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { "version": "4.52.2", @@ -1428,8 +1391,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.52.2", @@ -1443,8 +1405,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { "version": "4.52.2", @@ -1458,8 +1419,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { "version": "4.52.2", @@ -1473,8 +1433,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.52.2", @@ -1488,8 +1447,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.52.2", @@ -1503,8 +1461,7 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-openharmony-arm64": { "version": "4.52.2", @@ -1518,8 +1475,7 @@ "optional": true, "os": [ "openharmony" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { "version": "4.52.2", @@ -1533,8 +1489,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { "version": "4.52.2", @@ -1548,8 +1503,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { "version": "4.52.2", @@ -1563,8 +1517,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { "version": "4.52.2", @@ -1578,8 +1531,7 @@ "optional": true, "os": [ "win32" - ], - "peer": true + ] }, "node_modules/@skorotkiewicz/snowflake-id": { "version": "1.0.1", @@ -1874,8 +1826,7 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/accepts": { "version": "2.0.0", @@ -2219,7 +2170,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -2301,6 +2251,7 @@ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -2374,7 +2325,6 @@ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12.0.0" }, @@ -2636,6 +2586,7 @@ "integrity": "sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -2660,6 +2611,7 @@ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", "dev": true, "license": "MPL-2.0", + "peer": true, "dependencies": { "detect-libc": "^2.0.3" }, @@ -3031,7 +2983,6 @@ } ], "license": "MIT", - "peer": true, "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3131,8 +3082,7 @@ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", @@ -3244,7 +3194,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -3365,6 +3314,7 @@ "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -3389,6 +3339,7 @@ "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -3502,7 +3453,6 @@ "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -3772,7 +3722,8 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.13.tgz", "integrity": "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tailwindcss-animate": { "version": "1.0.7", @@ -3821,7 +3772,6 @@ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" @@ -3946,7 +3896,6 @@ "integrity": "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -4027,7 +3976,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } @@ -4091,6 +4039,7 @@ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/podman-compose.yml b/podman-compose.yml index d6ac08e..afe1387 100644 --- a/podman-compose.yml +++ b/podman-compose.yml @@ -2,28 +2,44 @@ version: "3.8" services: anvil: - build: - context: . - dockerfile: containers/foundry.Containerfile + image: ghcr.io/foundry-rs/foundry:latest command: ["/workspace/containers/anvil-entrypoint.sh"] volumes: - - .:/workspace:Z + - .:/workspace:z expose: - "8545" restart: unless-stopped + postgres: + image: docker.io/library/postgres:16-alpine + environment: + - POSTGRES_USER=ponder + - POSTGRES_PASSWORD=ponder_local + - POSTGRES_DB=ponder_local + volumes: + - postgres-data:/var/lib/postgresql/data + expose: + - "5432" + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ponder"] + interval: 5s + timeout: 5s + retries: 5 + bootstrap: - build: - context: . - dockerfile: containers/foundry.Containerfile + image: ghcr.io/foundry-rs/foundry:latest + user: "0:0" command: ["/workspace/containers/bootstrap.sh"] volumes: - - .:/workspace:Z + - .:/workspace:z environment: - ANVIL_RPC=http://anvil:8545 depends_on: anvil: condition: service_started + postgres: + condition: service_healthy restart: "no" ponder: @@ -38,7 +54,10 @@ services: environment: - CHOKIDAR_USEPOLLING=1 depends_on: - - anvil + anvil: + condition: service_started + postgres: + condition: service_healthy expose: - "42069" restart: unless-stopped @@ -98,7 +117,7 @@ services: caddy: image: docker.io/library/caddy:2.8 volumes: - - ./containers/Caddyfile:/etc/caddy/Caddyfile:Z + - ./containers/Caddyfile:/etc/caddy/Caddyfile:z ports: - "0.0.0.0:8081:80" depends_on: @@ -115,6 +134,7 @@ services: restart: unless-stopped volumes: + postgres-data: webapp-node-modules: landing-node-modules: ponder-node-modules: diff --git a/services/ponder/AGENTS.md b/services/ponder/AGENTS.md index 405fb0f..b9258aa 100644 --- a/services/ponder/AGENTS.md +++ b/services/ponder/AGENTS.md @@ -15,6 +15,7 @@ Ponder-based indexer that records Kraiken protocol activity and exposes the Grap ## Development Workflow - Primary path: `nohup ./scripts/local_env.sh start &` boots Anvil, deploys contracts, and launches Ponder in watch mode. +- Podman stack: `podman-compose up -d` starts all services including PostgreSQL; bootstrap creates `.env.local` automatically. - Focused debugging: within `services/ponder/`, run `npm install` then `PONDER_NETWORK=BASE_SEPOLIA_LOCAL_FORK npm run dev` once the stack is already online. - For production-style runs, use `npm run build` followed by `PONDER_NETWORK=BASE npm run start` and point `DATABASE_URL` to PostgreSQL if persistence is required. @@ -23,6 +24,18 @@ Ponder-based indexer that records Kraiken protocol activity and exposes the Grap - Regenerate typings after schema edits by restarting Ponder; generated artifacts live in `generated/`. - If the stack script fails during boot, check `.ponder/logs` and RPC quota usage before rerunning. +## Containerized Environment (Podman/Docker) +- **Environment Loading**: `.env.local` must be explicitly sourced in the entrypoint script before `npm run dev`. Ponder's built-in env loader may not find it in containerized environments. +- **Virtual Module Errors**: If you see `Failed to load url ponder:registry/ponder:schema/ponder:api`, check: + 1. `DATABASE_URL` is properly set and accessible + 2. kraiken-lib ABIs exist (`onchain/out/Kraiken.sol/Kraiken.json`) + 3. kraiken-lib TypeScript is built (`kraiken-lib/dist/index.js`) + 4. `ponder-env.d.ts` is writable by the container user (chmod 666 or pre-create it) + 5. Ponder version is 0.13.8+ (earlier versions had virtual module generation bugs) +- **PostgreSQL Connection**: Requires `DATABASE_URL` env var; Ponder falls back to PGlite if not set. The entrypoint must export this before Ponder starts. +- **File Permissions**: Container runs as `node` user; ensure `ponder-env.d.ts` is writable (created with 666 permissions on host). +- **API Endpoint Requirement**: Ponder 0.13.8+ requires `src/api/index.ts` to exist, even if you only use GraphQL. Missing it causes "API endpoint file not found" errors. + ## Quality Checks - Avoid reintroducing legacy subgraph assumptions; rely solely on Ponder schema helpers. - Keep handler logic deterministic and idempotent; reorgs trigger replays. diff --git a/services/ponder/package-lock.json b/services/ponder/package-lock.json index 202901b..5da6dc9 100644 --- a/services/ponder/package-lock.json +++ b/services/ponder/package-lock.json @@ -11,7 +11,7 @@ "@ponder/core": "^0.7.17", "hono": "^4.5.0", "kraiken-lib": "file:../../kraiken-lib", - "ponder": "^0.13.6", + "ponder": "^0.13.8", "viem": "^2.21.0" }, "devDependencies": { @@ -37,6 +37,7 @@ "@graphql-codegen/typescript-operations": "^4.2.0", "@graphql-typed-document-node/core": "^3.2.0", "@types/jest": "^29.5.12", + "@types/node": "^24.6.0", "jest": "^29.7.0", "ts-jest": "^29.1.2", "typescript": "^5.4.3" @@ -109,7 +110,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.2.13.tgz", "integrity": "sha512-YRY806NnScVqa21/1L1vaysSQ+0/cAva50z7vlwzaGiBOTS9JhdzIRHN0KfgMhobFAphbznZJ7urMso4RtMBIQ==", - "license": "Apache-2.0" + "license": "Apache-2.0", + "peer": true }, "node_modules/@envelop/core": { "version": "5.3.2", @@ -1011,6 +1013,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -1226,6 +1229,7 @@ "resolved": "https://registry.npmjs.org/kysely/-/kysely-0.26.3.tgz", "integrity": "sha512-yWSgGi9bY13b/W06DD2OCDDHQmq1kwTGYlQ4wpZkMOJqMGCstVCFIvxCCVG4KfY1/3G0MhDAcZsip/Lw8/vJWw==", "license": "MIT", + "peer": true, "engines": { "node": ">=14.0.0" } @@ -1609,6 +1613,7 @@ "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2139,6 +2144,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2971,6 +2977,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -3028,6 +3035,7 @@ "resolved": "https://registry.npmjs.org/hono/-/hono-4.9.8.tgz", "integrity": "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg==", "license": "MIT", + "peer": true, "engines": { "node": ">=16.9.0" } @@ -3967,6 +3975,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.9.1", "pg-pool": "^3.10.1", @@ -4123,9 +4132,9 @@ "license": "MIT" }, "node_modules/ponder": { - "version": "0.13.6", - "resolved": "https://registry.npmjs.org/ponder/-/ponder-0.13.6.tgz", - "integrity": "sha512-/LtSCfso7yshF7t7irNJyfQWvLfGI65quJiG4zaE902+coGm6YX9dqoVM+CxRtnOudSADTLsC3U3HAOJFfQXWQ==", + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/ponder/-/ponder-0.13.8.tgz", + "integrity": "sha512-Eqj4ZBpft6/WOZdbW1XIVoUsrdMtfZMEtfX7P4wBgqc1YCSHCOFtQmLUB2QxAe/Qw+sO+eiCCk+BDCCMEXlOYA==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.23.4", @@ -4326,6 +4335,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5017,6 +5027,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5080,6 +5091,7 @@ "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5145,6 +5157,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", @@ -5429,6 +5442,7 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", + "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/services/ponder/package.json b/services/ponder/package.json index 7d83666..7ab5434 100644 --- a/services/ponder/package.json +++ b/services/ponder/package.json @@ -13,7 +13,7 @@ "@ponder/core": "^0.7.17", "hono": "^4.5.0", "kraiken-lib": "file:../../kraiken-lib", - "ponder": "^0.13.6", + "ponder": "^0.13.8", "viem": "^2.21.0" }, "devDependencies": { diff --git a/services/ponder/ponder-env.d.ts b/services/ponder/ponder-env.d.ts index 7a6b15e..a1dfd97 100644 --- a/services/ponder/ponder-env.d.ts +++ b/services/ponder/ponder-env.d.ts @@ -9,12 +9,6 @@ declare module "ponder:schema" { export * from "./ponder.schema.ts"; } -declare module "*.json" { - import type { Abi } from 'viem' - const value: { abi: Abi } - export default value -} - // This file enables type checking and editor autocomplete for this Ponder project. // After upgrading, you may find that changes have been made to this file. // If this happens, please commit the changes. Do not manually edit this file. diff --git a/services/txnBot/AGENTS.md b/services/txnBot/AGENTS.md index f0b692f..8b8e30d 100644 --- a/services/txnBot/AGENTS.md +++ b/services/txnBot/AGENTS.md @@ -11,6 +11,8 @@ Automation service that maintains liquidity alignment and tax enforcement for th - `service.js` - Main loop that polls the GraphQL endpoint, evaluates triggers, and submits transactions. - `generateKey.js` - Utility for provisioning dedicated bot wallets. - `package.json` scripts - `npm install`, `npm start`, and debug runs via `DEBUG=* npm start`. +- **ES Module**: This service uses `"type": "module"` and imports kraiken-lib as an ES module. All imports use ES module syntax (`import` instead of `require()`). +- **Node.js Compatibility**: Requires Node.js ≥14 with ES module support. The `__dirname` and `__filename` globals are not available; use `import.meta.url` with `fileURLToPath()` instead. ## Configuration Set the following environment variables (automatically generated when the stack script is used): diff --git a/services/txnBot/package.json b/services/txnBot/package.json index c351639..e24a053 100644 --- a/services/txnBot/package.json +++ b/services/txnBot/package.json @@ -1,6 +1,7 @@ { "name": "marketMaker", "version": "0.0.1", + "type": "module", "main": "index.js", "license": "GPL3", "scripts": { diff --git a/services/txnBot/service.js b/services/txnBot/service.js index 8f9d1e3..72f1463 100644 --- a/services/txnBot/service.js +++ b/services/txnBot/service.js @@ -1,13 +1,19 @@ -const path = require('path'); +import path from 'path'; +import { fileURLToPath } from 'url'; +import dotenv from 'dotenv'; +import { ethers } from 'ethers'; +import express from 'express'; +import { decodePositionId } from 'kraiken-lib/ids'; +import { isPositionDelinquent } from 'kraiken-lib/staking'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + const dotenvPath = process.env.TXN_BOT_ENV_FILE ? path.resolve(process.env.TXN_BOT_ENV_FILE) : path.resolve(__dirname, '.env'); -require('dotenv').config({ path: dotenvPath }); -const { ethers } = require('ethers'); -const express = require('express'); -const { decodePositionId } = require('kraiken-lib/ids'); -const { isPositionDelinquent } = require('kraiken-lib/staking'); +dotenv.config({ path: dotenvPath }); const ACTIVE_POSITIONS_QUERY = ` query ActivePositions {