Skip to content

Changelog

Project + documentation history. Newest first. Add a one-line bullet for every code or doc change worth surfacing.

Format: YYYY-MM-DD — <area> — <one-line summary>

Areas: feat, fix, refactor, test, docs, chore, security.


2026-07-08 — full-egress IP privacy

  • securityall network egress now shares one proxy agent (src/utils/proxyAgent.js): ethers RPC over HTTP (FetchRequest agent) and WebSocket (ws factory + agent), the Solana connection (httpAgent), and axios (alerts/price). Previously only axios was proxied, so the RPC connection — the bot's most continuous trace — leaked the real IP even with a proxy set. Verified live: with HTTPS_PROXY set, the RPC host is tunnelled through the proxy, not connected directly. One switch (SOCKS_PROXY/HTTPS_PROXY) covers everything; fail-closed if the agent dep is missing.
  • securityPRICE_LOOKUP_ENABLED now defaults false (privacy-first): no CoinGecko holdings leak out of the box. It only affects cosmetic USD PnL in alerts, never triggers/automation.
  • security — added https-proxy-agent optional dep (alongside socks-proxy-agent) for the HTTP-proxy agent path.
  • testproxyAgent suite + updated httpClient suite (shared-agent model). Suite: 199 passing.
  • docs — safety.md §8a: RPC now proxied, CoinGecko off by default, "minimal setup for full IP privacy" (Tor in 3 steps).

2026-07-08 — security & privacy hardening

  • security — logs — no secret or key-bearing URL can reach the logs: new src/utils/redact.js (maskUrl/maskAddress/scrubSecrets) + a two-layer pino config (expanded key list incl. all *_RPC_*, + value-scrubbing serializers). Fixed a direct RPC-URL-with-API-key leak in solana.js. Verified live: 0 secret occurrences in logs/state.
  • security — identity — wallet address is masked (0x1234…5678) before it ever enters the dashboard store//api/state/SSE, unless DASHBOARD_SHOW_FULL_ADDRESS=true.
  • security — dashboard — CSP (connect-src 'self' blocks exfiltration), X-Frame-Options: DENY, nosniff, no-referrer, no-store on every response; configurable DASHBOARD_BIND (default 127.0.0.1) with a loud warning on non-loopback binds.
  • security — startup preflight (src/security/preflight.js): refuses to start in production when .env/keystore are group/world-readable (warns in dev); warns on identity/egress exposure (no proxy, exposed dashboard, CoinGecko holdings leak, raw key).
  • security — egress privacy — all outbound HTTP (Telegram/Discord/CoinGecko) routed through a shared client honoring SOCKS_PROXY (Tor, socks5h) / HTTPS_PROXY; fails closed if the proxy is configured but unavailable. PRICE_LOOKUP_ENABLED=false disables CoinGecko holdings leakage. DASHBOARD_PERSIST=false disables the plaintext trade log.
  • security — deps — overrode vulnerable ws (ethers transitive DoS) → 8.21.0 and form-data (CRLF) → 4.0.6. EVM-only install now audits 0 vulnerabilities; remaining advisories are all upstream in the optional Solana/Raydium SDK tree.
  • test — added redact, preflight, dashboardSecurity, httpClient suites (34 new tests). Suite: 195 passing.
  • docs — safety.md — new §8a Privacy & identity + rewritten §8 Logging.

  • fix — triggerEngine/index.js — positions are retired after a permanent executor failure (markPermanentlyFailed): no more endless re-fire/alert loop on errors that can never succeed (e.g. "No LP tokens to remove"). Dashboard shows failed-permanent; alert says the position will not re-fire. Regression test added.

  • feat — alerts — Telegram/Discord messages rebuilt: per-channel rendering (Telegram HTML with escaping — labels with underscores no longer break parsing; Discord markdown), human trigger reasons (single-swap vs cumulative move, volume ratio, position age, emergency tx), received amounts with symbols, PNL, explorer links, dry-run labeling, and retry-vs-retired guidance on errors.
  • feat — alerts/telegram — /status now actually replies with a live summary (mode, uptime, chain WS health, tx budget, every position with status/price/last-seen) instead of only logging. Built from the same store snapshot the dashboard uses.
  • feat — dashboard — UI rebuilt for at-a-glance visibility: DRY-RUN/LIVE badge, stat tiles (positions, chain WS health, tx budget vs caps, slippage/gas caps), armed-trigger chips, per-position cards with live price, last-swap/session deltas, drop-from-peak meter showing proximity to the price-drop trigger, price sparkline with hover tooltip (240-point rolling buffer added to the store), and a clearer event feed distinguishing retrying vs retired errors. Coverage/docs panels collapsed by default. Operational view is now dependency-free (works offline); CDN libs only for the docs viewer with plain-text fallback.

2026-07-07

  • fix — config.js — poolAddress was missing from PositionSchema, so zod silently stripped it and the V3 price monitor was disabled for every V3 position even when configured exactly as documented. Found during live mainnet verification of the audit fixes; regression test added.
  • fix — raydium.js — missing await on loadSolanaSigner(); the SDK was receiving a Promise as owner, breaking every live Solana removal.
  • fix — evm.js + monitors — WS reconnect now emits ws-down/ws-reconnected via providerEvents; PriceMonitor and EventMonitor re-establish their subscriptions on reconnect (previously the bot went silently blind after the first disconnect). Chain WS status surfaces on the dashboard.
  • feat — triggerEngine.js — price drop/rise now also fire on cumulative moves (drop from trailing peak, rise from first observation), catching gradual dumps a per-swap delta never sees.
  • feat — triggerEngine.js — max-age evaluated by a 30s timer sweep, so it fires even for pools with zero swap activity; engine.stop() added to shutdown.
  • fix — gas.js — base fee read from the latest block (previous derivation double-counted ethers' getFeeData convention); the gas-cap guard is now live: executors abort pre-broadcast when base+priority exceeds MAX_GAS_GWEI (previously the fee was silently clamped and the guard could never trip, risking indefinitely-stuck transactions). tx.wait bounded by TX_DEADLINE_SEC.
  • fix — priceMonitor.js — volume-spike baseline is now average volume per window-sized span (was per-swap mean), eliminating false fires on steady volume.
  • feat — BSC/PancakeSwap wired end to end: bsc chain in CHAINS/schema/position enum, BSC_RPC_* env vars, bscscan explorer URL, CoinGecko platform mapping, dex-first positionManager lookup.
  • fix — executors — emergency removals bypass the rate limiter (dead-man's switch no longer strands positions beyond MAX_TX_PER_MINUTE); EVM signers wrapped in ethers.NonceManager for concurrent removals; live mode preloads signers at boot.
  • fix — executors/index.js — permanent errors (blacklist, not-owner, empty position, post-broadcast failures) abort the retry loop instead of re-running the whole removal (a retry after broadcast could misreport a confirmed removal as failed).
  • fix — telegram.js — command backlog queued while the bot was down is discarded at startup (a stale /remove no longer executes on boot).
  • fix — keystore.js — fails fast when KEYSTORE_PASSWORD is unset and there is no TTY (previously hung forever on a readline prompt in Docker).
  • fix — priceMonitor.js — priceUnits now decimals-adjusted (human token1-per-token0 ratio); removed a wasted positions() RPC from the V3 monitor.
  • fix — alerts — dry-run success alerts no longer render tx/undefined; dry runs are labeled.
  • chore — eslint 9 flat config added (npm run lint was broken); CI workflow .github/workflows/test.yml (lint + tests); Dockerfile uses npm ci with the lockfile.
  • testtests/poc.findings.test.js: 6 proof-of-concept tests pinned the audit defects, then were flipped to regression tests (10 tests) when the fixes landed. Monitor resubscription + new gas/volume semantics covered in existing suites. Suite now 158 tests.
  • docs — testing.md, CLAUDE.md — synced with new trigger semantics, gas-cap contract, WS resubscription invariant, BSC support.

2026-05-13

  • docs — Initial /docs directory created (README, getting-started, usage, architecture, testing, safety, extending, CHANGELOG).
  • test — Keystore test suite now passes 9/9. Switched to encryptKeystoreJsonSync for correct ethers v6 API.
  • feat — UniswapV2 honeypot pre-flight via removeLiquidity.staticCall simulation (gap 1).
  • feat — Raydium price decoder via AmmRpcData pre-computed prices (gap 2).
  • feat — Raydium CPMM/CLMM dispatch + AMM v4 liquidity removal (gap 3).
  • test — Coverage now at 100 tests, ~87% branch. Suites: config, rateLimit, triggerEngine, priceMonitor, eventMonitor, executorDispatch, executorV2, executorV3, executorRaydium, approvalManager, keystore, gas, slippage, pnl.
  • fix — Telegram HTTP 400 on alert send. Caused by malformed TELEGRAM_CHAT_ID (leading colon). Stripped — delivery confirmed.

Template for future entries

## YYYY-MM-DD

- `feat` — <module> — short description.
- `fix` — <module> — short description.
- `docs` — <page touched> — short description.

Open-ended TODOs and known gaps live in docs/testing.md §3 and docs/architecture.md §7, not here. This file is for what happened, not what's pending.