fix: revm evaluator — UUPS bypass, deployedBytecode, graceful attack ops

- Skip UUPS upgradeTo: etch + vm.store ERC1967 implementation slot directly
  (OptimizerV3Push3 is standalone, no UUPS inheritance needed for evolution)
- Use deployedBytecode (runtime) instead of bytecode (creation) for vm.etch
- Inject transpiled body into OptimizerV3.sol (has getLiquidityParams via Optimizer)
  instead of using standalone OptimizerV3Push3.sol
- Wrap buy/sell/stake/unstake in try/catch — attack ops should not abort the batch
- Add /tmp read to fs_permissions for batch-eval manifest files
- Bootstrap recenter returns bool instead of reverting (soft-fail per candidate)
This commit is contained in:
openhands 2026-03-12 19:54:58 +00:00
parent b34e26eb70
commit 87bb5859e2
3 changed files with 97 additions and 30 deletions

View file

@ -39,7 +39,10 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
ONCHAIN_DIR="$REPO_ROOT/onchain"
TRANSPILER_DIR="$REPO_ROOT/tools/push3-transpiler"
TRANSPILER_OUT="$ONCHAIN_DIR/src/OptimizerV3Push3.sol"
ARTIFACT_PATH="$ONCHAIN_DIR/out/OptimizerV3Push3.sol/OptimizerV3Push3.json"
# Use OptimizerV3 (inherits Optimizer → UUPS compatible, has getLiquidityParams)
# instead of standalone OptimizerV3Push3 which lacks UUPS hooks.
OPTIMIZERV3_SOL="$ONCHAIN_DIR/src/OptimizerV3.sol"
ARTIFACT_PATH="$ONCHAIN_DIR/out/OptimizerV3.sol/OptimizerV3.json"
DEFAULT_ATTACKS_DIR="$ONCHAIN_DIR/script/backtesting/attacks"
# =============================================================================
@ -124,6 +127,64 @@ for PUSH3_FILE in "${PUSH3_FILES[@]}"; do
continue
fi
# Inject transpiled calculateParams body into OptimizerV3.sol (UUPS-compatible).
# Extract function body from OptimizerV3Push3.sol and replace content between
# BEGIN/END markers in OptimizerV3.sol.
python3 - "$TRANSPILER_OUT" "$OPTIMIZERV3_SOL" <<'PYEOF' || {
import sys
push3_path = sys.argv[1]
v3_path = sys.argv[2]
# Extract function body from OptimizerV3Push3.sol
# Find "function calculateParams" then extract everything between the opening { and
# the matching closing } at the same indent level (4 spaces / function level)
with open(push3_path) as f:
push3 = f.read()
# Find body start: line after "function calculateParams...{"
fn_start = push3.find("function calculateParams")
if fn_start == -1:
sys.exit("calculateParams not found in OptimizerV3Push3")
brace_start = push3.find("{", fn_start)
body_start = push3.index("\n", brace_start) + 1
# Find body end: the closing " }" of the function (4-space indent, before contract close)
# Walk backwards from end to find the function-level closing brace
lines = push3[body_start:].split("\n")
body_lines = []
for line in lines:
if line.strip() == "}" and (line.startswith(" }") or line == "}"):
# This is the function-closing brace
break
body_lines.append(line)
body = "\n".join(body_lines)
# Now inject into OptimizerV3.sol between markers
with open(v3_path) as f:
v3 = f.read()
begin_marker = "// ── BEGIN TRANSPILER OUTPUT"
end_marker = "// ── END TRANSPILER OUTPUT"
begin_idx = v3.find(begin_marker)
end_idx = v3.find(end_marker)
if begin_idx == -1 or end_idx == -1:
sys.exit("markers not found in OptimizerV3.sol")
begin_line_end = v3.index("\n", begin_idx) + 1
# Keep the end marker line intact
with open(v3_path, "w") as f:
f.write(v3[:begin_line_end])
f.write(body + "\n")
f.write(v3[end_idx:])
PYEOF
log "WARNING: failed to inject calculateParams into OptimizerV3.sol for $CANDIDATE_ID — skipping"
FAILED_IDS="$FAILED_IDS $CANDIDATE_ID"
continue
}
# Compile (forge's incremental build skips unchanged files quickly)
FORGE_EC=0
(cd "$ONCHAIN_DIR" && forge build --silent) >/dev/null 2>&1 || FORGE_EC=$?
@ -139,7 +200,7 @@ for PUSH3_FILE in "${PUSH3_FILES[@]}"; do
import json, sys
with open(sys.argv[1]) as f:
d = json.load(f)
bytecode = d["bytecode"]["object"]
bytecode = d["deployedBytecode"]["object"]
# Ensure 0x prefix
if not bytecode.startswith("0x"):
bytecode = "0x" + bytecode