fix/podman-postgres-integration (#37)

resolves #25

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: johba <johba@harb.eth>
Reviewed-on: https://codeberg.org/johba/harb/pulls/37
This commit is contained in:
johba 2025-10-01 20:26:49 +02:00
parent 8947ec11ca
commit b4c829e4d6
25 changed files with 187 additions and 144 deletions

1
.gitignore vendored
View file

@ -26,3 +26,4 @@ ponder-repo
tmp
foundry.lock
services/ponder/.env.local
node_modules

View file

@ -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.

View file

@ -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")"

View file

@ -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
}

View file

@ -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}

View file

@ -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..."
}

View file

@ -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.

View file

@ -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"
},

View file

@ -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"
],

View file

@ -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

View file

@ -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";

View file

@ -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;

View file

@ -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";

View file

@ -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;

View file

@ -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": [

View file

@ -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%).

115
package-lock.json generated
View file

@ -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"
}

View file

@ -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:

View file

@ -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.

View file

@ -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"
},

View file

@ -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": {

View file

@ -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.

View file

@ -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):

View file

@ -1,6 +1,7 @@
{
"name": "marketMaker",
"version": "0.0.1",
"type": "module",
"main": "index.js",
"license": "GPL3",
"scripts": {

View file

@ -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 {