diff --git a/scripts/harb-evaluator/red-team.sh b/scripts/harb-evaluator/red-team.sh index adf0f35..082f220 100755 --- a/scripts/harb-evaluator/red-team.sh +++ b/scripts/harb-evaluator/red-team.sh @@ -37,7 +37,6 @@ SWAP_ROUTER=0x94cC0AaC535CCDB3C01d6787D6413C739ae12bc4 V3_FACTORY=0x4752ba5DBc23f44D87826276BF6Fd6b1C372aD24 NPM=0x27F971cb582BF9E50F397e4d29a5C7A34f11faA2 POOL_FEE=10000 -FEE_DEST_CONST=0xf6a3eef9088A255c32b6aD2025f83E57291D9011 # ── Logging helpers ──────────────────────────────────────────────────────────── log() { echo "[red-team] $*"; } @@ -84,20 +83,37 @@ POOL=$("$CAST" call "$V3_FACTORY" "getPool(address,address,uint24)(address)" \ "$WETH" "$KRK" "$POOL_FEE" --rpc-url "$RPC_URL") log " Pool: $POOL" -# ── 3. Take Anvil snapshot (clean baseline) ──────────────────────────────────── +# ── 3. Grant recenterAccess to account 2 ────────────────────────────────────── +# Done BEFORE the snapshot so every revert restores account 2's access. +# LM.recenterAccess is a single address slot — replace it with account 2. +# Only the feeDestination is authorised to call setRecenterAccess(). +log "Granting recenterAccess to account 2 ($RECENTER_ADDR) ..." +FEE_DEST=$("$CAST" call "$LM" "feeDestination()(address)" --rpc-url "$RPC_URL") \ + || die "Failed to read feeDestination() from LM" +FEE_DEST=$(echo "$FEE_DEST" | tr -d '[:space:]') +"$CAST" rpc --rpc-url "$RPC_URL" anvil_impersonateAccount "$FEE_DEST" \ + || die "anvil_impersonateAccount $FEE_DEST failed" +"$CAST" send --rpc-url "$RPC_URL" --from "$FEE_DEST" --unlocked \ + "$LM" "setRecenterAccess(address)" "$RECENTER_ADDR" >/dev/null 2>&1 \ + || die "setRecenterAccess($RECENTER_ADDR) failed — check that feeDestination is correct" +"$CAST" rpc --rpc-url "$RPC_URL" anvil_stopImpersonatingAccount "$FEE_DEST" \ + || die "anvil_stopImpersonatingAccount $FEE_DEST failed" +log " recenterAccess granted" + +# ── 4. Take Anvil snapshot (clean baseline, includes recenterAccess grant) ───── log "Taking Anvil snapshot..." SNAP=$("$CAST" rpc anvil_snapshot --rpc-url "$RPC_URL" | tr -d '"') log " Snapshot ID: $SNAP" -# ── 4. Grant recenterAccess to account 2 (if not already) ───────────────────── -# LM.recenterAccess is a single slot — we replace it with account 2. -# The fee destination is the only address that can call setRecenterAccess(). -log "Granting recenterAccess to account 2 ($RECENTER_ADDR) ..." -"$CAST" rpc --rpc-url "$RPC_URL" anvil_impersonateAccount "$FEE_DEST_CONST" >/dev/null 2>&1 -"$CAST" send --rpc-url "$RPC_URL" --from "$FEE_DEST_CONST" --unlocked \ - "$LM" "setRecenterAccess(address)" "$RECENTER_ADDR" >/dev/null 2>&1 \ - || log " WARNING: setRecenterAccess failed — account 2 may already hold access or differ" -"$CAST" rpc --rpc-url "$RPC_URL" anvil_stopImpersonatingAccount "$FEE_DEST_CONST" >/dev/null 2>&1 +# Revert to the baseline snapshot on exit so subsequent runs start clean. +cleanup() { + local rc=$? + if [[ -n "${SNAP:-}" ]]; then + "$CAST" rpc anvil_revert "$SNAP" --rpc-url "$RPC_URL" >/dev/null 2>&1 || true + fi + exit $rc +} +trap cleanup EXIT INT TERM # ── Helper: compute ethPerToken (mirrors floor.ts getEthPerToken) ────────────── # ethPerToken = (lm_native_eth + lm_weth) * 1e18 / adjusted_outstanding_supply @@ -216,13 +232,14 @@ Only recenterAccess account can call it. ### Staking \`Stake.snatch(assets, receiver, taxRateIndex, positionsToSnatch)\` -- taxRateIndex: 0–4 (index into TAX_RATES array — not a raw percentage) +- taxRateIndex: 0–29 (index into the 30-element TAX_RATES array — not a raw percentage) - KRK staked is held by the Stake contract (excluded from adjusted_supply) - KRK in Stake does NOT count against the floor denominator ### outstandingSupply() vs totalSupply() -\`KRK.outstandingSupply()\` ≈ totalSupply but counts all KRK regardless of where it is. -The floor helper subtracts Stake and feeDestination balances to get adjusted_supply. +\`KRK.outstandingSupply() = totalSupply() - balanceOf(liquidityManager)\` +LM-held KRK (in pool positions) is excluded from outstandingSupply. +The floor formula then additionally subtracts KRK at Stake and feeDestination to get adjusted_supply. --- @@ -233,8 +250,10 @@ The floor helper subtracts Stake and feeDestination balances to get adjusted_sup LM_ETH=\$(/home/debian/.foundry/bin/cast balance ${LM} --rpc-url http://localhost:8545) LM_WETH=\$(/home/debian/.foundry/bin/cast call ${WETH} "balanceOf(address)(uint256)" ${LM} --rpc-url http://localhost:8545) SUPPLY=\$(/home/debian/.foundry/bin/cast call ${KRK} "outstandingSupply()(uint256)" --rpc-url http://localhost:8545) +FEE_DEST=\$(/home/debian/.foundry/bin/cast call ${LM} "feeDestination()(address)" --rpc-url http://localhost:8545) +FEE_BAL=\$(/home/debian/.foundry/bin/cast call ${KRK} "balanceOf(address)(uint256)" \$FEE_DEST --rpc-url http://localhost:8545) STAKE_BAL=\$(/home/debian/.foundry/bin/cast call ${KRK} "balanceOf(address)(uint256)" ${STAKE} --rpc-url http://localhost:8545) -python3 -c "e=\$LM_ETH; w=\$LM_WETH; s=\$SUPPLY; k=\$STAKE_BAL; adj=s-k; print('ethPerToken:', (e+w)*10**18//adj if adj>0 else 0, 'wei/token')" +python3 -c "e=\$LM_ETH; w=\$LM_WETH; s=\$SUPPLY; f=\$FEE_BAL; k=\$STAKE_BAL; adj=s-f-k; print('ethPerToken:', (e+w)*10**18//adj if adj>0 else 0, 'wei/token')" \`\`\` ### Wrap ETH to WETH @@ -385,7 +404,7 @@ log "Spawning Claude red-team agent (timeout: ${CLAUDE_TIMEOUT}s)..." log " Report will be written to: $REPORT" set +e -CLAUDE_TIMEOUT="$CLAUDE_TIMEOUT" claude -p --dangerously-skip-permissions \ +timeout "$CLAUDE_TIMEOUT" claude -p --dangerously-skip-permissions \ "$PROMPT" >"$REPORT" 2>&1 AGENT_EXIT=$? set -e