recorded fuzzing
This commit is contained in:
parent
85cc8191be
commit
7eef96f366
25 changed files with 56 additions and 6468 deletions
|
|
@ -30,6 +30,9 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
LiquidityManager lm;
|
||||
bool token0isWeth;
|
||||
|
||||
// Reusable swap executor to avoid repeated deployments
|
||||
SwapExecutor swapExecutor;
|
||||
|
||||
address account = makeAddr("trader");
|
||||
address whale = makeAddr("whale");
|
||||
address feeDestination = makeAddr("fees");
|
||||
|
|
@ -87,15 +90,23 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
vm.prank(whale);
|
||||
weth.deposit{value: whaleFund}();
|
||||
|
||||
// Create SwapExecutor once per scenario to avoid repeated deployments
|
||||
swapExecutor = new SwapExecutor(pool, weth, harberg, token0isWeth);
|
||||
|
||||
uint256 initialBalance = weth.balanceOf(account);
|
||||
|
||||
// Initial recenter
|
||||
vm.prank(feeDestination);
|
||||
lm.recenter();
|
||||
|
||||
// Initialize position tracking
|
||||
// Initialize position tracking (skip CSV init if already done)
|
||||
if (trackPositions) {
|
||||
initializePositionsCSV();
|
||||
if (seed == 0) {
|
||||
// Only initialize CSV for first seed if not already initialized
|
||||
if (bytes(csv).length == 0) {
|
||||
initializePositionsCSV();
|
||||
}
|
||||
}
|
||||
_recordPositionData("Initial");
|
||||
}
|
||||
|
||||
|
|
@ -402,21 +413,19 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
function _executeBuy(address buyer, uint256 amount) internal virtual {
|
||||
if (amount == 0 || weth.balanceOf(buyer) < amount) return;
|
||||
|
||||
SwapExecutor executor = new SwapExecutor(pool, weth, harberg, token0isWeth);
|
||||
vm.prank(buyer);
|
||||
weth.transfer(address(executor), amount);
|
||||
weth.transfer(address(swapExecutor), amount);
|
||||
|
||||
try executor.executeBuy(amount, buyer) {} catch {}
|
||||
try swapExecutor.executeBuy(amount, buyer) {} catch {}
|
||||
}
|
||||
|
||||
function _executeSell(address seller, uint256 amount) internal virtual {
|
||||
if (amount == 0 || harberg.balanceOf(seller) < amount) return;
|
||||
|
||||
SwapExecutor executor = new SwapExecutor(pool, weth, harberg, token0isWeth);
|
||||
vm.prank(seller);
|
||||
harberg.transfer(address(executor), amount);
|
||||
harberg.transfer(address(swapExecutor), amount);
|
||||
|
||||
try executor.executeSell(amount, seller) {} catch {}
|
||||
try swapExecutor.executeSell(amount, seller) {} catch {}
|
||||
}
|
||||
|
||||
function _getOptimizerByClass(string memory class) internal returns (address) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
ScenarioRecorder public recorder;
|
||||
bool public enableRecording = true;
|
||||
string public runId; // Unique identifier for this run
|
||||
bool private isRecordingProfitable; // Only record if scenario is profitable
|
||||
|
||||
// Constructor removed - set trackPositions in run() instead
|
||||
|
||||
// Store actions for current scenario
|
||||
struct ActionRecord {
|
||||
|
|
@ -32,42 +35,29 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
// Get run ID from environment or generate default
|
||||
runId = vm.envOr("RUN_ID", string("LOCAL"));
|
||||
|
||||
// Force position tracking to be enabled for CSV generation
|
||||
vm.setEnv("TRACK_POSITIONS", "true");
|
||||
|
||||
console.log("=== RECORDED Fuzzing Analysis ===");
|
||||
console.log(string.concat("Run ID: ", runId));
|
||||
console.log("Recording enabled for profitable scenario replay");
|
||||
|
||||
super.run();
|
||||
}
|
||||
|
||||
// Override to add recording for profitable scenarios
|
||||
function _runImprovedScenario(uint256 seed) internal override returns (uint256 finalBalance, bool reachedDiscovery) {
|
||||
// Initialize recorder for this scenario
|
||||
if (enableRecording) {
|
||||
recorder = new ScenarioRecorder();
|
||||
// Always track positions when recording to generate CSV for visualizer
|
||||
trackPositions = true;
|
||||
recorder.initializeScenario(
|
||||
seed,
|
||||
optimizerClass,
|
||||
200 ether, // LM initial ETH
|
||||
weth.balanceOf(account) + weth.balanceOf(whale), // Total trader ETH
|
||||
token0isWeth,
|
||||
10000 // Pool fee
|
||||
);
|
||||
delete currentActions; // Clear previous scenario
|
||||
}
|
||||
// Clear previous scenario
|
||||
delete currentActions;
|
||||
isRecordingProfitable = false;
|
||||
|
||||
// Record initial state
|
||||
_recordState("INITIAL", address(0), 0);
|
||||
|
||||
// Run the scenario with recording
|
||||
// Run the base scenario
|
||||
(finalBalance, reachedDiscovery) = super._runImprovedScenario(seed);
|
||||
|
||||
// Record final state
|
||||
_recordState("FINAL", address(0), 0);
|
||||
|
||||
// If profitable, export the recording
|
||||
uint256 initialBalance = 50 ether; // Approximate initial
|
||||
if (finalBalance > initialBalance && enableRecording) {
|
||||
_exportScenario(seed, initialBalance, finalBalance, reachedDiscovery);
|
||||
// Export summary if profitable
|
||||
uint256 initialBalance = 50 ether;
|
||||
if (finalBalance > initialBalance && reachedDiscovery) {
|
||||
_exportSummary(seed, initialBalance, finalBalance, reachedDiscovery);
|
||||
}
|
||||
|
||||
return (finalBalance, reachedDiscovery);
|
||||
|
|
@ -79,22 +69,13 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
|
||||
(, int24 tickBefore,,,,,) = pool.slot0();
|
||||
|
||||
// Record pre-state
|
||||
if (enableRecording) {
|
||||
_recordState("PRE_BUY", buyer, amount);
|
||||
recorder.recordBuy(buyer, amount, 0, 0);
|
||||
}
|
||||
|
||||
// Execute trade
|
||||
super._executeBuy(buyer, amount);
|
||||
|
||||
(, int24 tickAfter,,,,,) = pool.slot0();
|
||||
|
||||
// Record post-state and action
|
||||
if (enableRecording) {
|
||||
_recordState("POST_BUY", buyer, amount);
|
||||
_recordAction("BUY", buyer, amount, tickBefore, tickAfter);
|
||||
}
|
||||
// Only record action summary (not full state)
|
||||
_recordAction("BUY", buyer, amount, tickBefore, tickAfter);
|
||||
}
|
||||
|
||||
function _executeSell(address seller, uint256 amount) internal override {
|
||||
|
|
@ -102,22 +83,13 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
|
||||
(, int24 tickBefore,,,,,) = pool.slot0();
|
||||
|
||||
// Record pre-state
|
||||
if (enableRecording) {
|
||||
_recordState("PRE_SELL", seller, amount);
|
||||
recorder.recordSell(seller, amount, 0, 0);
|
||||
}
|
||||
|
||||
// Execute trade
|
||||
super._executeSell(seller, amount);
|
||||
|
||||
(, int24 tickAfter,,,,,) = pool.slot0();
|
||||
|
||||
// Record post-state and action
|
||||
if (enableRecording) {
|
||||
_recordState("POST_SELL", seller, amount);
|
||||
_recordAction("SELL", seller, amount, tickBefore, tickAfter);
|
||||
}
|
||||
// Only record action summary (not full state)
|
||||
_recordAction("SELL", seller, amount, tickBefore, tickAfter);
|
||||
}
|
||||
|
||||
// Override each strategy to add recenter recording
|
||||
|
|
@ -163,25 +135,19 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
function _executeRecenter() internal {
|
||||
(, int24 tickBefore,,,,,) = pool.slot0();
|
||||
|
||||
if (enableRecording) {
|
||||
_recordState("PRE_RECENTER", feeDestination, 0);
|
||||
recorder.recordRecenter(feeDestination, tx.gasprice);
|
||||
}
|
||||
|
||||
vm.warp(block.timestamp + 1 hours);
|
||||
vm.prank(feeDestination);
|
||||
try lm.recenter() {} catch {}
|
||||
|
||||
(, int24 tickAfter,,,,,) = pool.slot0();
|
||||
|
||||
if (enableRecording) {
|
||||
_recordState("POST_RECENTER", feeDestination, 0);
|
||||
_recordAction("RECENTER", feeDestination, 0, tickBefore, tickAfter);
|
||||
}
|
||||
// Only record action summary
|
||||
_recordAction("RECENTER", feeDestination, 0, tickBefore, tickAfter);
|
||||
}
|
||||
|
||||
function _recordState(string memory label, address actor, uint256 amount) internal {
|
||||
if (!enableRecording) return;
|
||||
// Only record states if we're exporting a profitable scenario
|
||||
if (!isRecordingProfitable || !enableRecording) return;
|
||||
|
||||
(uint160 sqrtPriceX96, int24 currentTick,,,,,) = pool.slot0();
|
||||
|
||||
|
|
@ -193,17 +159,6 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
0, // VWAP placeholder
|
||||
harberg.outstandingSupply()
|
||||
);
|
||||
|
||||
// Also record optimizer params if during recenter
|
||||
if (keccak256(bytes(label)) == keccak256("POST_RECENTER")) {
|
||||
// Get optimizer params (simplified - would need actual values)
|
||||
recorder.recordOptimizerParams(
|
||||
5e17, // capitalInefficiency placeholder
|
||||
5e17, // anchorShare placeholder
|
||||
50, // anchorWidth placeholder
|
||||
5e17 // discoveryDepth placeholder
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _recordAction(
|
||||
|
|
@ -225,7 +180,7 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
}));
|
||||
}
|
||||
|
||||
function _exportScenario(
|
||||
function _exportSummary(
|
||||
uint256 seed,
|
||||
uint256 initialBalance,
|
||||
uint256 finalBalance,
|
||||
|
|
@ -235,23 +190,6 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
console.log(string.concat(" Run ID: ", runId));
|
||||
console.log(string.concat(" Seed: ", vm.toString(seed)));
|
||||
|
||||
// Export as JSON
|
||||
string memory json = recorder.exportToJson();
|
||||
string memory jsonFilename = string.concat(
|
||||
"recorded_scenario_seed", vm.toString(seed), ".json"
|
||||
);
|
||||
vm.writeFile(jsonFilename, json);
|
||||
console.log(string.concat(" JSON exported to: ", jsonFilename));
|
||||
|
||||
// Also export simplified replay script
|
||||
string memory replayScript = _generateReplayScript(seed);
|
||||
string memory scriptFilename = string.concat(
|
||||
"replay_script_seed", vm.toString(seed), ".sol"
|
||||
);
|
||||
vm.writeFile(scriptFilename, replayScript);
|
||||
console.log(string.concat(" Replay script exported to: ", scriptFilename));
|
||||
|
||||
// Export action summary with Run ID
|
||||
string memory summary = _generateActionSummary(
|
||||
seed,
|
||||
initialBalance,
|
||||
|
|
@ -263,6 +201,15 @@ contract RecordedFuzzingAnalysis is ImprovedFuzzingAnalysis {
|
|||
);
|
||||
vm.writeFile(summaryFilename, summary);
|
||||
console.log(string.concat(" Summary exported to: ", summaryFilename));
|
||||
|
||||
if (currentActions.length > 0) {
|
||||
string memory replayScript = _generateReplayScript(seed);
|
||||
string memory scriptFilename = string.concat(
|
||||
"replay_script_seed", vm.toString(seed), ".sol"
|
||||
);
|
||||
vm.writeFile(scriptFilename, replayScript);
|
||||
console.log(string.concat(" Replay script exported to: ", scriptFilename));
|
||||
}
|
||||
}
|
||||
|
||||
function _generateReplayScript(uint256 seed) internal view returns (string memory) {
|
||||
|
|
|
|||
|
|
@ -59,10 +59,10 @@ echo -e "${YELLOW}Starting recorded fuzzing analysis...${NC}"
|
|||
FUZZING_RUNS=$RUNS_VALUE \
|
||||
OPTIMIZER_CLASS=$OPTIMIZER \
|
||||
RUN_ID=$RUN_ID \
|
||||
forge script analysis/RecordedFuzzingAnalysis.s.sol:RecordedFuzzingAnalysis --disable-block-gas-limit -vv 2>&1 | tee $OUTPUT_DIR/fuzzing.log
|
||||
forge script analysis/RecordedFuzzingAnalysis.s.sol:RecordedFuzzingAnalysis --gas-limit 1000000000 -vv 2>&1 | tee $OUTPUT_DIR/fuzzing.log
|
||||
|
||||
# Check for generated scenario files
|
||||
SCENARIO_COUNT=$(ls -1 recorded_scenario_*.json 2>/dev/null | wc -l)
|
||||
# Check for generated scenario files (check for summaries, they're always generated)
|
||||
SCENARIO_COUNT=$(ls -1 scenario_summary_seed*.txt 2>/dev/null | wc -l)
|
||||
|
||||
if [ $SCENARIO_COUNT -gt 0 ]; then
|
||||
echo ""
|
||||
|
|
@ -176,6 +176,7 @@ if [ $POSITION_CSV_COUNT -gt 0 ] && [ $SCENARIO_COUNT -gt 0 ]; then
|
|||
fi
|
||||
# Use absolute path for the symlink
|
||||
ln -s "$(realpath $FIRST_CSV)" "$TEMP_LINK"
|
||||
echo "Created symlink: $TEMP_LINK -> $FIRST_CSV"
|
||||
|
||||
# Check if server is already running on common ports
|
||||
SERVER_RUNNING=false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue