fix: address review findings in red-team.sh (#520)
- Move snapshot to after setRecenterAccess so agent reverts restore recenterAccess for account 2 on every retry - Read feeDestination() dynamically from LM (removes hardcoded constant) and add || die guards on impersonation calls - Add EXIT/INT/TERM cleanup trap that reverts to the baseline snapshot - Fix agent floor-check snippet: add FEE_DEST/FEE_BAL reads so formula matches compute_eth_per_token (adj=s-f-k, not adj=s-k) - Use `timeout "$CLAUDE_TIMEOUT"` to enforce wall-clock process limit - Correct taxRateIndex range: 0-29 (30-element TAX_RATES array) - Fix outstandingSupply() description: excludes LM-held KRK, not all KRK Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
23d460542b
commit
ea53e4cfce
1 changed files with 35 additions and 16 deletions
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue