Commit graph

13 commits

Author SHA1 Message Date
johba
0b6442a87c fix: Attack files have hardcoded tokenIds that are fork-block-sensitive (#614)
Make burn_lp ops fork-block-independent by using a 1-based positionIndex
(resolved at runtime from prior mint_lp ops) instead of hardcoded NFT
tokenIds. Mirrors the existing pattern used by unstake/_stakedPositionIds.

Also log a warning when burn_lp encounters zero liquidity instead of
silently becoming a no-op.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 10:11:58 +00:00
openhands
9b157883b4 fix: AttackRunner.s.sol: V3_FACTORY still hardcoded to Base mainnet (#953)
Make V3_FACTORY injectable via vm.envOr("V3_FACTORY", DEFAULT_V3_FACTORY),
preserving the Base mainnet address as the default for existing fork runs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 10:40:33 +00:00
openhands
13f406b5a9 fix: red-team.sh and AttackRunner.s.sol still use Base mainnet addresses (#939)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 07:33:54 +00:00
openhands
fd912a2a69 fix: AttackRunner.s.sol NPM_ADDR last byte is 0xF1 but scripts use 0xF3 (#807)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 00:50:28 +00:00
openhands
dbf78de793 fix: bootstrap + red-team on forked networks
Bootstrap fixes:
- Idempotency check: skip if Kraiken already deployed on Anvil
- anvil_setCode to strip ERC-4337 code from deployer + feeDest
- DeployLocal.sol: feeDest derived from keccak256('harb.local.feeDest')

Red-team fixes:
- New bootstrap-light.sh: Anvil-only, ~30s deploy
- red-team.sh uses bootstrap-light instead of full docker compose
- anvil_setBalance for feeDest before impersonation
- forge --color never, path resolution, docker chown

Address fixes (all Base mainnet, in both FitnessEvaluator + AttackRunner):
- V3_FACTORY: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
- SWAP_ROUTER: 0x2626664c2603336E57B271c5C0b26F421741e481
- NPM_ADDR: 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1
2026-03-14 13:31:23 +00:00
openhands
bc2ed50451 fix: V3_FACTORY address lacks a source comment (#730)
Add inline Basescan URL comment identifying V3_FACTORY as the Uniswap V3
Factory on Base mainnet, consistent with the existing comment style used
for NPM_ADDR in both files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 06:57:02 +00:00
openhands
cbab4c36da fix: NPM_ADDR may be Base Sepolia address in both files (#686)
Replace 0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 (Base Sepolia
NonfungiblePositionManager) with the correct Base mainnet address
0x03a520B32c04bf3beef7BEb72E919cF822Ed34F3 in all four files that
referenced it, and add an inline comment citing the chain and source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 02:22:51 +00:00
openhands
fe385fb010 fix: fix: il-crystallization-80 attack times out (153 steps) (#597)
Add `buy_recenter_loop` batch op to AttackRunner — executes N×(buy→recenter)
cycles in a single Solidity loop, emitting snapshots after each recenter.
Rewrite il-crystallization-80.jsonl from 153 individual JSONL steps to 2 lines
using the new op with count=80, matching the intended attack name. Also corrects
the cycle count from 76 (previous file) to the intended 80.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:16:41 +00:00
openhands
acfdd2b22e fix: fix: Debug failing round-trip-safe attack in evolution fitness (#595)
After a buy→sell round-trip the net price movement is near zero, so
recenter() reverts with "amplitude not reached" and aborts the whole
AttackRunner script.

Wrap the recenter() call in a try/catch so amplitude failures are
caught and logged as a skipped step rather than propagating as a fatal
revert.  When recenter is skipped, no state snapshot is emitted and the
attack sequence continues — matching the intended semantics: round-trip
trading should not cause the fitness scorer to crash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 07:57:31 +00:00
openhands
78246ed399 fix: fix: Debug failing staking-safe attack in evolution fitness (#596)
Stake.nextPositionId starts at 654_321, so attack files cannot use literal
on-chain IDs (e.g. positionId=1 always reverts with PositionNotFound).

Fix AttackRunner to treat the JSONL positionId field as a 1-based index into
the list of positions created by stake ops during the current run:
- Add IStake.snatch returns (uint256) to the interface so the returned ID is
  captured.
- Track returned IDs in _stakedPositionIds[] (inserted in creation order).
- _executeUnstake resolves positionId to _stakedPositionIds[positionId-1]
  before calling exitPosition, matching the natural "unstake position 1"
  semantics in the attack DSL.

KRK approval for Stake was already present in _setup(); no other changes needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 07:10:13 +00:00
openhands
d6ca28ae32 fix: AttackRunner round-3 review findings
- **Bug**: Fix JSON malformation in _snapshotPositions — closing literal was '"}}}' (three
  braces) but only '"}}'  is needed (close discovery{} + positions{}). The third brace
  prematurely closed the root object, making every snapshot unparseable downstream.

- **Nit**: _executeStake local variable renamed taxRateIndex → taxRate to match the
  IStake interface and Stake.sol. JSONL field key '.taxRateIndex' is kept for backward
  compatibility with existing attack files; the comment and NatDoc header now say so.

- **Nit**: recenter_is_up now emits JSON null (not false) before the first recenter call,
  via a new _hasRecentered flag. Downstream parsers can distinguish "no recenter yet"
  from "last recenter moved price down" (false). _hasRecentered is set to true alongside
  _lastRecenterIsUp in the recenter handler.

- **Nit**: Added a comment to _logSnapshot explaining that pool.slot0() is a view call
  and forge-std finalises broadcast state before executing it, so tick/sqrtPrice are
  always post-broadcast accurate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 17:51:18 +00:00
openhands
297442083d fix: AttackRunner review findings — TVL accuracy, recenter capture, discovery ethValue
- **Bug**: `_positionEthValue` now sums both the ETH component and the KRK component
  (converted to ETH via `FullMath.mulDiv` at current sqrtPriceX96) so `lm_eth_total`
  correctly reflects LM TVL for all price ranges (below/in/above range).

- **Bug**: `recenter()` return value (`bool isUp` — price direction) is now captured in
  `_lastRecenterIsUp` state variable and emitted as `"recenter_is_up"` in every snapshot.
  Note: `recenter()` reverts on failure; `false` means price moved *down*, not a no-op.

- **Bug**: Discovery position now emits `"ethValue"` in its snapshot JSON object,
  matching the floor and anchor fields for symmetric automated parsing.

- **Warning**: `IStake.snatch` interface parameter renamed `taxRateIndex` → `taxRate` to
  match the actual `Stake.sol` signature (the value is a raw rate, not a lookup index).

- **Warning**: Unknown op codes in the JSONL file now emit a `console.log` warning
  instead of silently skipping, catching typos in attack sequences.

- **Nit**: `_setup()` now wraps 9 000 ETH (up from 1 000) to cover heavy buy sequences
  that would otherwise exhaust the adversary's WETH.

- **Nit**: `_computeVwapTick` documents the int128 overflow guard and its tick=0 sentinel
  meaning so callers can distinguish "VWAP unavailable" from tick zero.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 17:14:34 +00:00
openhands
c8453f6a33 fix: Backtesting: replay red-team attack sequences against optimizer candidates (#536)
- Add AttackRunner.s.sol: structured forge script that reads attack ops from a
  JSONL file (ATTACK_FILE env), executes them against the local Anvil deployment,
  and emits full state snapshots (tick, positions, VWAP, optimizer output,
  adversary balances) as JSON lines after every recenter and at start/end.

- Add 5 canonical attack files in onchain/script/backtesting/attacks/:
  * il-crystallization-15.jsonl  — 15 buy-recenter cycles + sell (extraction)
  * il-crystallization-80.jsonl  — 80 buy-recenter cycles + sell (extraction)
  * fee-drain-oscillation.jsonl  — buy-recenter-sell-recenter oscillation
  * round-trip-safe.jsonl        — 20 full round-trips (regression: safe)
  * staking-safe.jsonl           — staking manipulation (regression: safe)

- Add scripts/harb-evaluator/export-attacks.py: parses red-team-stream.jsonl
  for tool_use Bash blocks containing cast send commands and converts them to
  AttackRunner-compatible JSONL (buy/sell/recenter/stake/unstake/mint_lp/burn_lp).

- Update scripts/harb-evaluator/red-team.sh: after each agent run, automatically
  exports the attack sequence via export-attacks.py and replays it with
  AttackRunner to capture structured snapshots in tmp/red-team-snapshots.jsonl.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 02:08:06 +00:00