consolidated scripts
This commit is contained in:
parent
10702f5aa3
commit
74e09bb38b
5 changed files with 383 additions and 377 deletions
|
|
@ -47,9 +47,15 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
uint256 public totalSnatchesAttempted;
|
||||
uint256 public totalSnatchesSucceeded;
|
||||
|
||||
// Staking tracking
|
||||
mapping(address => uint256[]) public activePositions;
|
||||
uint256[] public allPositionIds; // Track all positions for snatching
|
||||
// OPTIMIZATION: Circular buffer for position tracking (max 50 positions)
|
||||
uint256 constant MAX_TRACKED_POSITIONS = 50;
|
||||
uint256[MAX_TRACKED_POSITIONS] public trackedPositions;
|
||||
uint256 public positionWriteIndex;
|
||||
uint256 public totalPositionsCreated;
|
||||
mapping(uint256 => address) public positionOwners;
|
||||
|
||||
// Counter to limit CSV recording and prevent memory issues
|
||||
uint256 private csvRecordCount;
|
||||
|
||||
// Configuration
|
||||
uint256 public fuzzingRuns;
|
||||
|
|
@ -78,9 +84,10 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
// Get optimizer
|
||||
address optimizerAddress = _getOptimizerByClass(optimizerClass);
|
||||
|
||||
// Track profitable scenarios
|
||||
// Track profitable scenarios (limit string concatenation to prevent memory issues)
|
||||
string memory profitableCSV = "Scenario,Seed,Initial Balance,Final Balance,Profit,Profit %,Discovery Reached\n";
|
||||
uint256 profitableCount;
|
||||
uint256 maxProfitableRecords = 20; // Limit to prevent memory issues
|
||||
|
||||
for (uint256 seed = 0; seed < fuzzingRuns; seed++) {
|
||||
if (seed % 10 == 0 && seed > 0) {
|
||||
|
|
@ -118,9 +125,13 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
|
||||
// Initialize position tracking for each seed
|
||||
if (trackPositions) {
|
||||
// Initialize CSV header for each seed (after clearCSV from previous run)
|
||||
// Reset tracking for new scenario
|
||||
_resetPositionTracking();
|
||||
clearCSV(); // Clear any previous data
|
||||
csvRecordCount = 0; // Reset counter
|
||||
initializePositionsCSV();
|
||||
_recordPositionData("Initial");
|
||||
csvRecordCount = 1; // Count the initial record
|
||||
}
|
||||
|
||||
// Run improved trading scenario
|
||||
|
|
@ -141,16 +152,19 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
console.log(string.concat(" Profit: ", vm.toString(profit / 1e15), " finney (", vm.toString(profitPct), "%)"));
|
||||
console.log(string.concat(" Discovery reached: ", reachedDiscovery ? "YES" : "NO"));
|
||||
|
||||
profitableCSV = string.concat(
|
||||
profitableCSV,
|
||||
optimizerClass, ",",
|
||||
vm.toString(seed), ",",
|
||||
vm.toString(initialBalance), ",",
|
||||
vm.toString(finalBalance), ",",
|
||||
vm.toString(profit), ",",
|
||||
vm.toString(profitPct), ",",
|
||||
reachedDiscovery ? "true" : "false", "\n"
|
||||
);
|
||||
// Only record limited profitable scenarios to prevent memory issues
|
||||
if (profitableCount < maxProfitableRecords) {
|
||||
profitableCSV = string.concat(
|
||||
profitableCSV,
|
||||
optimizerClass, ",",
|
||||
vm.toString(seed), ",",
|
||||
vm.toString(initialBalance), ",",
|
||||
vm.toString(finalBalance), ",",
|
||||
vm.toString(profit), ",",
|
||||
vm.toString(profitPct), ",",
|
||||
reachedDiscovery ? "true" : "false", "\n"
|
||||
);
|
||||
}
|
||||
profitableCount++;
|
||||
}
|
||||
|
||||
|
|
@ -286,9 +300,9 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
}
|
||||
}
|
||||
|
||||
// Only record every 5th trade to avoid memory issues with large trade counts
|
||||
if (trackPositions && i % 5 == 0) {
|
||||
_recordPositionData("Trade");
|
||||
// Record position data for visualization (always record all trades)
|
||||
if (trackPositions) {
|
||||
_recordPositionDataSafe(string.concat("T", vm.toString(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -331,16 +345,26 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
optimizerClass = vm.envOr("OPTIMIZER_CLASS", string("BullMarketOptimizer"));
|
||||
}
|
||||
|
||||
function _recordPositionData(string memory label) internal {
|
||||
// Split into separate function calls to avoid stack too deep
|
||||
_recordPositionDataInternal(label);
|
||||
// Safe wrapper that prevents gas and memory issues
|
||||
function _recordPositionDataSafe(string memory label) internal {
|
||||
// Only record if we have enough gas remaining
|
||||
if (gasleft() < 1_000_000) {
|
||||
return; // Skip recording if low on gas
|
||||
}
|
||||
|
||||
// Skip if CSV is getting extremely large (increased limit for all trades)
|
||||
if (bytes(csv).length > 100000) {
|
||||
return; // Skip to avoid memory issues
|
||||
}
|
||||
|
||||
csvRecordCount++;
|
||||
_recordPositionData(label);
|
||||
}
|
||||
|
||||
function _recordPositionDataInternal(string memory label) private {
|
||||
// Disable position tracking if it causes memory issues
|
||||
if (bytes(csv).length > 50000) {
|
||||
// CSV is getting too large, skip recording
|
||||
return;
|
||||
function _recordPositionData(string memory label) internal {
|
||||
// Absolutely prevent recording if buffer is too large
|
||||
if (bytes(csv).length > 100000) {
|
||||
return; // Stop recording to prevent memory issues
|
||||
}
|
||||
|
||||
(,int24 currentTick,,,,,) = pool.slot0();
|
||||
|
|
@ -350,221 +374,205 @@ contract ImprovedFuzzingAnalysis is Test, CSVManager {
|
|||
(uint128 anchorLiq, int24 anchorLower, int24 anchorUpper) = lm.positions(ThreePositionStrategy.Stage.ANCHOR);
|
||||
(uint128 discoveryLiq, int24 discoveryLower, int24 discoveryUpper) = lm.positions(ThreePositionStrategy.Stage.DISCOVERY);
|
||||
|
||||
// Use simpler row building to avoid memory issues
|
||||
string memory row = label;
|
||||
row = string.concat(row, ",", vm.toString(currentTick));
|
||||
row = string.concat(row, ",", vm.toString(floorLower));
|
||||
row = string.concat(row, ",", vm.toString(floorUpper));
|
||||
row = string.concat(row, ",", vm.toString(floorLiq));
|
||||
row = string.concat(row, ",", vm.toString(anchorLower));
|
||||
row = string.concat(row, ",", vm.toString(anchorUpper));
|
||||
row = string.concat(row, ",", vm.toString(anchorLiq));
|
||||
row = string.concat(row, ",", vm.toString(discoveryLower));
|
||||
row = string.concat(row, ",", vm.toString(discoveryUpper));
|
||||
row = string.concat(row, ",", vm.toString(discoveryLiq));
|
||||
row = string.concat(row, ",", token0isWeth ? "true" : "false");
|
||||
row = string.concat(row, ",", vm.toString(stake.getPercentageStaked()));
|
||||
row = string.concat(row, ",", vm.toString(stake.getAverageTaxRate()));
|
||||
// Skip staking data for trades to save memory (only get for key events)
|
||||
uint256 pctStaked = 0;
|
||||
uint256 avgTax = 0;
|
||||
if (enableStaking && bytes(label).length > 1 && bytes(label)[0] != 0x54) { // Not a "T" trade label
|
||||
try stake.getPercentageStaked{gas: 30000}() returns (uint256 pct) {
|
||||
pctStaked = pct;
|
||||
} catch {}
|
||||
try stake.getAverageTaxRate{gas: 30000}() returns (uint256 tax) {
|
||||
avgTax = tax;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Build CSV row with minimal concatenations
|
||||
string memory row = string.concat(
|
||||
label, ",",
|
||||
vm.toString(currentTick), ",",
|
||||
vm.toString(floorLower), ","
|
||||
);
|
||||
|
||||
row = string.concat(
|
||||
row,
|
||||
vm.toString(floorUpper), ",",
|
||||
vm.toString(floorLiq), ","
|
||||
);
|
||||
|
||||
row = string.concat(
|
||||
row,
|
||||
vm.toString(anchorLower), ",",
|
||||
vm.toString(anchorUpper), ","
|
||||
);
|
||||
|
||||
row = string.concat(
|
||||
row,
|
||||
vm.toString(anchorLiq), ",",
|
||||
vm.toString(discoveryLower), ","
|
||||
);
|
||||
|
||||
row = string.concat(
|
||||
row,
|
||||
vm.toString(discoveryUpper), ",",
|
||||
vm.toString(discoveryLiq), ","
|
||||
);
|
||||
|
||||
row = string.concat(
|
||||
row,
|
||||
token0isWeth ? "1" : "0", ",",
|
||||
vm.toString(pctStaked), ",",
|
||||
vm.toString(avgTax)
|
||||
);
|
||||
|
||||
appendCSVRow(row);
|
||||
}
|
||||
|
||||
function _executeStakingAction(uint256 rand) internal {
|
||||
uint256 action = rand % 100;
|
||||
|
||||
// 70% chance to stake, 5% chance to exit (to fill pool faster)
|
||||
if (action < 70) {
|
||||
_executeStake(rand);
|
||||
} else if (action < 75) {
|
||||
_executeExitPosition(rand);
|
||||
}
|
||||
}
|
||||
|
||||
function _executeStakeWithAmount(address staker, uint256 amount, uint32 taxRate) internal {
|
||||
// Direct stake with specific amount and tax rate
|
||||
_doStake(staker, amount, taxRate);
|
||||
}
|
||||
|
||||
function _executeStake(uint256 rand) internal {
|
||||
address staker = rand % 2 == 0 ? account : whale;
|
||||
uint256 harbBalance = harberg.balanceOf(staker);
|
||||
|
||||
if (harbBalance > harberg.minStake()) {
|
||||
// Stake between 30% and 80% of balance (increased from 10-50%)
|
||||
uint256 amount = harbBalance * (30 + (rand % 50)) / 100;
|
||||
if (amount < harberg.minStake()) {
|
||||
amount = harberg.minStake();
|
||||
}
|
||||
|
||||
// Random tax rate (index 0-29)
|
||||
uint32 taxRate = uint32(rand % 30);
|
||||
|
||||
vm.prank(staker);
|
||||
harberg.approve(address(stake), amount);
|
||||
_doStake(staker, amount, taxRate);
|
||||
|
||||
totalStakesAttempted++;
|
||||
vm.prank(staker);
|
||||
try stake.snatch{gas: 10_000_000}(amount, staker, taxRate, new uint256[](0)) returns (uint256 positionId) {
|
||||
totalStakesSucceeded++;
|
||||
_addTrackedPosition(positionId, staker);
|
||||
console.log(" STAKED:", amount / 1e18, "KRAIKEN");
|
||||
if (trackPositions) {
|
||||
_recordPositionDataSafe("Stake");
|
||||
}
|
||||
} catch {
|
||||
// If regular stake fails, try snatching with higher tax rate
|
||||
if (totalPositionsCreated > 0) {
|
||||
_tryOptimizedSnatch(staker, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _doStake(address staker, uint256 amount, uint32 taxRate) internal {
|
||||
vm.startPrank(staker);
|
||||
|
||||
// Check current pool capacity before attempting stake
|
||||
uint256 currentPercentStaked = stake.getPercentageStaked();
|
||||
|
||||
// First try to stake without snatching
|
||||
totalStakesAttempted++;
|
||||
try stake.snatch(amount, staker, taxRate, new uint256[](0)) returns (uint256 positionId) {
|
||||
totalStakesSucceeded++;
|
||||
activePositions[staker].push(positionId);
|
||||
allPositionIds.push(positionId);
|
||||
console.log(" STAKED:", amount / 1e18);
|
||||
console.log(" Tax rate index:", taxRate);
|
||||
console.log(" Pool now at:", currentPercentStaked / 1e16);
|
||||
function _tryOptimizedSnatch(address staker, uint256 amount) internal {
|
||||
uint32 maxTaxRate = 29;
|
||||
|
||||
// Find up to 3 snatchable positions quickly
|
||||
uint256[] memory toSnatch = _findSnatchableQuick(maxTaxRate, amount);
|
||||
|
||||
if (toSnatch.length > 0) {
|
||||
totalSnatchesAttempted++;
|
||||
vm.prank(staker);
|
||||
try stake.snatch{gas: 15_000_000}(amount, staker, maxTaxRate, toSnatch) returns (uint256 positionId) {
|
||||
totalSnatchesSucceeded++;
|
||||
_addTrackedPosition(positionId, staker);
|
||||
console.log(" SNATCHED", toSnatch.length, "positions");
|
||||
if (trackPositions) {
|
||||
_recordPositionData("Stake");
|
||||
_recordPositionDataSafe("Snatch");
|
||||
}
|
||||
} catch Error(string memory reason) {
|
||||
// If staking failed (likely pool is full), try snatching with max tax rate
|
||||
console.log(" Stake failed:", reason);
|
||||
console.log(" Pool at:", currentPercentStaked / 1e16, "percent - attempting snatch...");
|
||||
|
||||
// Use max tax rate (29) for snatching
|
||||
uint32 maxTaxRate = 29;
|
||||
|
||||
// Find positions to snatch (those with lower tax rates)
|
||||
uint256[] memory positionsToSnatch = _findSnatchablePositions(maxTaxRate, amount);
|
||||
|
||||
if (positionsToSnatch.length > 0) {
|
||||
totalSnatchesAttempted++;
|
||||
try stake.snatch(amount, staker, maxTaxRate, positionsToSnatch) returns (uint256 positionId) {
|
||||
totalSnatchesSucceeded++;
|
||||
activePositions[staker].push(positionId);
|
||||
allPositionIds.push(positionId);
|
||||
|
||||
// Remove snatched positions from tracking
|
||||
_removeSnatchedPositions(positionsToSnatch);
|
||||
|
||||
console.log(" SNATCHED positions:", positionsToSnatch.length);
|
||||
console.log(" Staked amount:", amount / 1e18);
|
||||
if (trackPositions) {
|
||||
_recordPositionData("Snatch");
|
||||
}
|
||||
} catch Error(string memory sReason) {
|
||||
console.log(" Snatching failed:", sReason);
|
||||
} catch {
|
||||
console.log(" Snatching failed with unknown error");
|
||||
}
|
||||
} else {
|
||||
console.log(" No snatchable positions found");
|
||||
}
|
||||
} catch {
|
||||
// Catch-all for non-string errors
|
||||
console.log(" Stake failed (unknown error) at", currentPercentStaked / 1e16, "percent");
|
||||
}
|
||||
vm.stopPrank();
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
function _executeExitPosition(uint256 rand) internal {
|
||||
address staker = rand % 2 == 0 ? account : whale;
|
||||
|
||||
if (activePositions[staker].length > 0) {
|
||||
uint256 index = rand % activePositions[staker].length;
|
||||
uint256 positionId = activePositions[staker][index];
|
||||
|
||||
// Find a position owned by this staker
|
||||
uint256 positionToExit = _findOwnedPosition(staker);
|
||||
|
||||
if (positionToExit > 0) {
|
||||
vm.prank(staker);
|
||||
try stake.exitPosition(positionId) {
|
||||
// Remove from array
|
||||
activePositions[staker][index] = activePositions[staker][activePositions[staker].length - 1];
|
||||
activePositions[staker].pop();
|
||||
|
||||
// Also remove from allPositionIds
|
||||
for (uint256 j = 0; j < allPositionIds.length; j++) {
|
||||
if (allPositionIds[j] == positionId) {
|
||||
allPositionIds[j] = 0; // Mark as removed
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try stake.exitPosition{gas: 5_000_000}(positionToExit) {
|
||||
_removeTrackedPosition(positionToExit);
|
||||
if (trackPositions) {
|
||||
_recordPositionData("ExitStake");
|
||||
_recordPositionDataSafe("Unstake");
|
||||
}
|
||||
} catch {
|
||||
// Exit failed (position might be liquidated), remove from tracking
|
||||
activePositions[staker][index] = activePositions[staker][activePositions[staker].length - 1];
|
||||
activePositions[staker].pop();
|
||||
|
||||
// Also remove from allPositionIds
|
||||
for (uint256 j = 0; j < allPositionIds.length; j++) {
|
||||
if (allPositionIds[j] == positionId) {
|
||||
allPositionIds[j] = 0; // Mark as removed
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Position might be already exited/snatched
|
||||
_removeTrackedPosition(positionToExit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _findSnatchablePositions(uint32 maxTaxRate, uint256 amountNeeded) internal view returns (uint256[] memory) {
|
||||
// Find positions with tax rates lower than maxTaxRate
|
||||
uint256[] memory snatchable = new uint256[](10); // Max 10 positions to snatch
|
||||
function _findSnatchableQuick(uint32 maxTaxRate, uint256 amountNeeded) internal view returns (uint256[] memory) {
|
||||
uint256[] memory result = new uint256[](3); // Max 3 positions
|
||||
uint256 count = 0;
|
||||
uint256 totalShares = 0;
|
||||
|
||||
for (uint256 i = 0; i < allPositionIds.length && count < 10; i++) {
|
||||
uint256 positionId = allPositionIds[i];
|
||||
// Check only recent positions (last 20 in circular buffer)
|
||||
uint256 checkCount = totalPositionsCreated < 20 ? totalPositionsCreated : 20;
|
||||
uint256 startIdx = positionWriteIndex > checkCount ? positionWriteIndex - checkCount : 0;
|
||||
|
||||
for (uint256 i = 0; i < checkCount && count < 3; i++) {
|
||||
uint256 idx = (startIdx + i) % MAX_TRACKED_POSITIONS;
|
||||
uint256 positionId = trackedPositions[idx];
|
||||
if (positionId == 0) continue;
|
||||
|
||||
// Get position info
|
||||
(uint256 shares, address owner,, , uint32 taxRate) = stake.positions(positionId);
|
||||
(uint256 shares,, , , uint32 taxRate) = stake.positions(positionId);
|
||||
|
||||
// Skip if position doesn't exist or tax rate is too high
|
||||
if (shares == 0 || taxRate >= maxTaxRate) continue;
|
||||
|
||||
snatchable[count] = positionId;
|
||||
totalShares += shares;
|
||||
count++;
|
||||
|
||||
// Check if we have enough shares to cover the amount needed
|
||||
uint256 assetsFromShares = stake.sharesToAssets(totalShares);
|
||||
if (assetsFromShares >= amountNeeded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Resize array to actual count
|
||||
uint256[] memory result = new uint256[](count);
|
||||
for (uint256 i = 0; i < count; i++) {
|
||||
result[i] = snatchable[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function _removeSnatchedPositions(uint256[] memory snatchedIds) internal {
|
||||
// Remove snatched positions from allPositionIds
|
||||
for (uint256 i = 0; i < snatchedIds.length; i++) {
|
||||
uint256 snatchedId = snatchedIds[i];
|
||||
|
||||
// Find and remove from allPositionIds
|
||||
for (uint256 j = 0; j < allPositionIds.length; j++) {
|
||||
if (allPositionIds[j] == snatchedId) {
|
||||
allPositionIds[j] = 0; // Mark as removed
|
||||
if (shares > 0 && taxRate < maxTaxRate) {
|
||||
result[count] = positionId;
|
||||
totalShares += shares;
|
||||
count++;
|
||||
|
||||
if (stake.sharesToAssets(totalShares) >= amountNeeded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resize result
|
||||
uint256[] memory finalResult = new uint256[](count);
|
||||
for (uint256 i = 0; i < count; i++) {
|
||||
finalResult[i] = result[i];
|
||||
}
|
||||
|
||||
return finalResult;
|
||||
}
|
||||
|
||||
// Helper functions for circular buffer position tracking
|
||||
function _addTrackedPosition(uint256 positionId, address owner) internal {
|
||||
trackedPositions[positionWriteIndex] = positionId;
|
||||
positionOwners[positionId] = owner;
|
||||
positionWriteIndex = (positionWriteIndex + 1) % MAX_TRACKED_POSITIONS;
|
||||
totalPositionsCreated++;
|
||||
}
|
||||
|
||||
function _removeTrackedPosition(uint256 positionId) internal {
|
||||
delete positionOwners[positionId];
|
||||
// Don't remove from circular buffer, just mark owner as deleted
|
||||
}
|
||||
|
||||
function _resetPositionTracking() internal {
|
||||
// Clear circular buffer
|
||||
for (uint256 i = 0; i < MAX_TRACKED_POSITIONS; i++) {
|
||||
trackedPositions[i] = 0;
|
||||
}
|
||||
positionWriteIndex = 0;
|
||||
totalPositionsCreated = 0;
|
||||
}
|
||||
|
||||
function _findOwnedPosition(address owner) internal view returns (uint256) {
|
||||
// Check last 10 positions for ownership
|
||||
uint256 checkCount = totalPositionsCreated < 10 ? totalPositionsCreated : 10;
|
||||
uint256 startIdx = positionWriteIndex > checkCount ? positionWriteIndex - checkCount : 0;
|
||||
|
||||
for (uint256 i = 0; i < checkCount; i++) {
|
||||
uint256 idx = (startIdx + i) % MAX_TRACKED_POSITIONS;
|
||||
uint256 positionId = trackedPositions[idx];
|
||||
|
||||
// Remove from owner's activePositions
|
||||
for (uint256 k = 0; k < 2; k++) {
|
||||
address owner = k == 0 ? account : whale;
|
||||
uint256[] storage ownerPositions = activePositions[owner];
|
||||
for (uint256 m = 0; m < ownerPositions.length; m++) {
|
||||
if (ownerPositions[m] == snatchedId) {
|
||||
ownerPositions[m] = ownerPositions[ownerPositions.length - 1];
|
||||
ownerPositions.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (positionId > 0 && positionOwners[positionId] == owner) {
|
||||
return positionId;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,8 @@ This directory contains tools for fuzzing the KRAIKEN LiquidityManager to identi
|
|||
## Files
|
||||
|
||||
### Core Scripts
|
||||
- `FuzzingAnalysis.s.sol` - Main Solidity fuzzing script that tests trading scenarios
|
||||
- `run-fuzzing.sh` - Shell script to orchestrate multiple fuzzing runs
|
||||
- `ImprovedFuzzingAnalysis.s.sol` - Enhanced fuzzing script with staking support and memory optimizations
|
||||
- `run-fuzzing.sh` - Shell script to orchestrate multiple fuzzing runs with configurable parameters
|
||||
- `clean.sh` - Cleanup script to remove generated files
|
||||
|
||||
### Helpers
|
||||
|
|
@ -47,19 +47,23 @@ This directory contains tools for fuzzing the KRAIKEN LiquidityManager to identi
|
|||
|
||||
```bash
|
||||
# Basic usage
|
||||
./run-fuzzing.sh <optimizer_class> [runs=N] [trades=N]
|
||||
./run-fuzzing.sh <optimizer_class> [runs=N] [staking=on|off] [buybias=N] [trades=N] [stakingbias=N]
|
||||
|
||||
# Examples
|
||||
./run-fuzzing.sh BullMarketOptimizer # Uses defaults
|
||||
./run-fuzzing.sh WhaleOptimizer runs=100 # 100 runs
|
||||
./run-fuzzing.sh BearMarketOptimizer trades=50 # 50 trades per run
|
||||
./run-fuzzing.sh NeutralMarketOptimizer runs=25 trades=30 # Both params
|
||||
./run-fuzzing.sh BullMarketOptimizer runs=50 staking=on buybias=85 trades=100 stakingbias=95 # Full config
|
||||
```
|
||||
|
||||
Parameters:
|
||||
- `optimizer_class` - Required. The optimizer class to use
|
||||
- `runs=N` - Optional. Number of fuzzing runs (default: 50)
|
||||
- `trades=N` - Optional. Trades per run (default: 20, actual will be ±5)
|
||||
- `runs=N` - Optional. Number of fuzzing runs (default: 20)
|
||||
- `staking=on|off` - Optional. Enable/disable staking (default: on)
|
||||
- `buybias=N` - Optional. 0-100% bias towards buying vs selling (default: 50)
|
||||
- `trades=N` - Optional. Trades per run (default: 15, supports 100+ trades)
|
||||
- `stakingbias=N` - Optional. 0-100% bias towards staking vs unstaking (default: 80)
|
||||
|
||||
### Output
|
||||
|
||||
|
|
@ -96,19 +100,25 @@ The fuzzing script supports these environment variables:
|
|||
- `FUZZING_RUNS` - Number of runs (overridden by script parameter)
|
||||
- `OPTIMIZER_CLASS` - Optimizer to use (overridden by script parameter)
|
||||
- `TRADES_PER_RUN` - Trades per run (overridden by script parameter)
|
||||
- `TRACK_POSITIONS` - Enable detailed position tracking (default: false)
|
||||
- `TRACK_POSITIONS` - Enable detailed position tracking (default: true)
|
||||
- `ENABLE_STAKING` - Enable staking operations (default: true)
|
||||
- `BUY_BIAS` - Buy bias percentage (default: 50)
|
||||
- `STAKING_BIAS` - Staking bias percentage (default: 80)
|
||||
|
||||
## Development
|
||||
|
||||
To add a new optimizer:
|
||||
1. Create the optimizer contract in `../test/mocks/`
|
||||
2. Import it in `FuzzingAnalysis.s.sol`
|
||||
2. Import it in `ImprovedFuzzingAnalysis.s.sol`
|
||||
3. Add it to the `_getOptimizerByClass` function
|
||||
4. Update this README
|
||||
|
||||
## Notes
|
||||
|
||||
- Each run deploys a fresh Uniswap V3 environment
|
||||
- Gas limit is set to 200M for --via-ir compilation
|
||||
- Gas limit is set to 200M for script execution
|
||||
- Results are deterministic based on the seed
|
||||
- The fuzzer tests random buy/sell patterns with periodic recenters
|
||||
- The fuzzer tests random buy/sell patterns with periodic recenters
|
||||
- Supports staking operations with position snatching mechanics
|
||||
- Memory-optimized with circular buffer for position tracking
|
||||
- Records all trades to CSV for complete visualization
|
||||
|
|
@ -1,12 +1,162 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This script now uses the improved recorded fuzzing system
|
||||
# The old FuzzingAnalysis.s.sol has been replaced with RecordedFuzzingAnalysis.s.sol
|
||||
# which uses larger trades that can actually reach the discovery position
|
||||
# Usage: ./run-improved-fuzzing.sh [optimizer] [runs=N] [staking=on|off] [buybias=N] [trades=N] [stakingbias=N]
|
||||
# Examples:
|
||||
# ./run-improved-fuzzing.sh BullMarketOptimizer runs=50
|
||||
# ./run-improved-fuzzing.sh WhaleOptimizer runs=20 staking=off
|
||||
# ./run-improved-fuzzing.sh BullMarketOptimizer runs=200 staking=on buybias=100 trades=30 stakingbias=95
|
||||
|
||||
echo "Note: run-fuzzing.sh now uses the improved fuzzing with recording capabilities"
|
||||
echo "Redirecting to run-recorded-fuzzing.sh..."
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Configuration
|
||||
OPTIMIZER=${1:-BullMarketOptimizer}
|
||||
RUNS=${2:-runs=20}
|
||||
STAKING=${3:-staking=on}
|
||||
BUYBIAS=${4:-buybias=50}
|
||||
TRADES=${5:-trades=15}
|
||||
STAKINGBIAS=${6:-stakingbias=80}
|
||||
|
||||
# Parse runs parameter
|
||||
if [[ $RUNS == runs=* ]]; then
|
||||
RUNS_VALUE=${RUNS#runs=}
|
||||
else
|
||||
RUNS_VALUE=$RUNS
|
||||
fi
|
||||
|
||||
# Parse staking parameter
|
||||
STAKING_ENABLED="true"
|
||||
if [[ $STAKING == staking=* ]]; then
|
||||
STAKING_VALUE=${STAKING#staking=}
|
||||
if [[ $STAKING_VALUE == "off" ]] || [[ $STAKING_VALUE == "false" ]] || [[ $STAKING_VALUE == "0" ]]; then
|
||||
STAKING_ENABLED="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Parse buy bias parameter
|
||||
BUYBIAS_VALUE="50"
|
||||
if [[ $BUYBIAS == buybias=* ]]; then
|
||||
BUYBIAS_VALUE=${BUYBIAS#buybias=}
|
||||
fi
|
||||
|
||||
# Parse trades parameter
|
||||
TRADES_VALUE="15"
|
||||
if [[ $TRADES == trades=* ]]; then
|
||||
TRADES_VALUE=${TRADES#trades=}
|
||||
fi
|
||||
|
||||
# Parse staking bias parameter
|
||||
STAKINGBIAS_VALUE="80"
|
||||
if [[ $STAKINGBIAS == stakingbias=* ]]; then
|
||||
STAKINGBIAS_VALUE=${STAKINGBIAS#stakingbias=}
|
||||
fi
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUTPUT_DIR="fuzzing_results_${OPTIMIZER}_${TIMESTAMP}"
|
||||
|
||||
echo -e "${GREEN}=== Fuzzing Analysis ===${NC}"
|
||||
echo "Optimizer: $OPTIMIZER"
|
||||
echo "Total runs: $RUNS_VALUE"
|
||||
echo "Trades per run: $TRADES_VALUE"
|
||||
echo "Staking: $([ "$STAKING_ENABLED" = "true" ] && echo "enabled" || echo "disabled")"
|
||||
if [ "$STAKING_ENABLED" = "true" ]; then
|
||||
echo "Staking bias: $STAKINGBIAS_VALUE%"
|
||||
fi
|
||||
echo "Buy bias: $BUYBIAS_VALUE%"
|
||||
echo "Output directory: $OUTPUT_DIR"
|
||||
echo ""
|
||||
|
||||
# Pass all arguments to the new script
|
||||
exec ./analysis/run-recorded-fuzzing.sh "$@"
|
||||
# Validate optimizer
|
||||
case $OPTIMIZER in
|
||||
BullMarketOptimizer|BearMarketOptimizer|NeutralMarketOptimizer|WhaleOptimizer|ExtremeOptimizer|MaliciousOptimizer)
|
||||
echo "Optimizer validation passed"
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Error: Invalid optimizer class${NC}"
|
||||
echo "Valid options: BullMarketOptimizer, BearMarketOptimizer, NeutralMarketOptimizer, WhaleOptimizer, ExtremeOptimizer, MaliciousOptimizer"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create output directory
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
# Run the fuzzing analysis
|
||||
echo -e "${YELLOW}Starting fuzzing analysis...${NC}"
|
||||
echo ""
|
||||
|
||||
FUZZING_RUNS=$RUNS_VALUE \
|
||||
OPTIMIZER_CLASS=$OPTIMIZER \
|
||||
ENABLE_STAKING=$STAKING_ENABLED \
|
||||
BUY_BIAS=$BUYBIAS_VALUE \
|
||||
TRADES_PER_RUN=$TRADES_VALUE \
|
||||
STAKING_BIAS=$STAKINGBIAS_VALUE \
|
||||
TRACK_POSITIONS=true \
|
||||
forge script analysis/ImprovedFuzzingAnalysis.s.sol:ImprovedFuzzingAnalysis --gas-limit 200000000 -vv 2>&1 | tee $OUTPUT_DIR/fuzzing.log
|
||||
|
||||
# Extract key metrics
|
||||
echo ""
|
||||
echo -e "${GREEN}=== ANALYSIS COMPLETE ===${NC}"
|
||||
|
||||
# Show summary from log
|
||||
tail -20 $OUTPUT_DIR/fuzzing.log | grep -E "Total scenarios|Profitable|Discovery|Stakes|Snatches" || true
|
||||
|
||||
# Check for position CSVs
|
||||
POSITION_CSV_COUNT=$(ls -1 improved_positions_*.csv 2>/dev/null | wc -l)
|
||||
|
||||
if [ $POSITION_CSV_COUNT -gt 0 ]; then
|
||||
# Move position CSVs to output directory
|
||||
mv improved_positions_*.csv $OUTPUT_DIR/
|
||||
echo ""
|
||||
echo -e "${GREEN}Position CSVs saved to: $OUTPUT_DIR/${NC}"
|
||||
|
||||
# Calculate max staking percentage
|
||||
echo ""
|
||||
echo -e "${YELLOW}=== Maximum Staking Level ===${NC}"
|
||||
for f in $OUTPUT_DIR/improved_positions_*.csv; do
|
||||
tail -1 "$f" | cut -d',' -f13
|
||||
done | sort -n | tail -1 | awk '{
|
||||
pct = $1/1e16
|
||||
printf "%.2f%% of authorized stake (%.2f%% of KRAIKEN supply)\n", pct, pct*0.2
|
||||
}'
|
||||
fi
|
||||
|
||||
# Check for snatching activity
|
||||
echo ""
|
||||
echo -e "${YELLOW}=== Snatching Activity ===${NC}"
|
||||
SNATCH_COUNT=$(grep -c "SNATCHED" $OUTPUT_DIR/fuzzing.log 2>/dev/null || true)
|
||||
if [ -z "$SNATCH_COUNT" ]; then
|
||||
SNATCH_COUNT="0"
|
||||
fi
|
||||
if [ "$SNATCH_COUNT" -gt "0" ]; then
|
||||
echo -e "${GREEN}Snatching observed! Found $SNATCH_COUNT snatching events${NC}"
|
||||
grep "SNATCHED" $OUTPUT_DIR/fuzzing.log | head -5
|
||||
else
|
||||
echo "No snatching observed in this run"
|
||||
fi
|
||||
|
||||
# Check for profitable scenarios
|
||||
PROFITABLE_COUNT=$(grep -c "PROFITABLE!" $OUTPUT_DIR/fuzzing.log 2>/dev/null || true)
|
||||
if [ -z "$PROFITABLE_COUNT" ]; then
|
||||
PROFITABLE_COUNT="0"
|
||||
fi
|
||||
if [ "$PROFITABLE_COUNT" -gt "0" ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}=== PROFITABLE SCENARIOS FOUND ===${NC}"
|
||||
echo "Found $PROFITABLE_COUNT profitable scenarios"
|
||||
grep "PROFITABLE!" $OUTPUT_DIR/fuzzing.log | head -5
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Full results saved to: $OUTPUT_DIR/${NC}"
|
||||
echo ""
|
||||
echo "To view detailed logs:"
|
||||
echo " cat $OUTPUT_DIR/fuzzing.log"
|
||||
echo ""
|
||||
echo "To visualize position movements (if CSVs generated):"
|
||||
echo " ./analysis/view-scenarios.sh $OUTPUT_DIR"
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Usage: ./run-improved-fuzzing.sh [optimizer] [runs=N] [staking=on|off] [buybias=N] [trades=N] [stakingbias=N]
|
||||
# Examples:
|
||||
# ./run-improved-fuzzing.sh BullMarketOptimizer runs=50
|
||||
# ./run-improved-fuzzing.sh WhaleOptimizer runs=20 staking=off
|
||||
# ./run-improved-fuzzing.sh BullMarketOptimizer runs=200 staking=on buybias=100 trades=30 stakingbias=95
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Configuration
|
||||
OPTIMIZER=${1:-BullMarketOptimizer}
|
||||
RUNS=${2:-runs=20}
|
||||
STAKING=${3:-staking=on}
|
||||
BUYBIAS=${4:-buybias=50}
|
||||
TRADES=${5:-trades=15}
|
||||
STAKINGBIAS=${6:-stakingbias=80}
|
||||
|
||||
# Parse runs parameter
|
||||
if [[ $RUNS == runs=* ]]; then
|
||||
RUNS_VALUE=${RUNS#runs=}
|
||||
else
|
||||
RUNS_VALUE=$RUNS
|
||||
fi
|
||||
|
||||
# Parse staking parameter
|
||||
STAKING_ENABLED="true"
|
||||
if [[ $STAKING == staking=* ]]; then
|
||||
STAKING_VALUE=${STAKING#staking=}
|
||||
if [[ $STAKING_VALUE == "off" ]] || [[ $STAKING_VALUE == "false" ]] || [[ $STAKING_VALUE == "0" ]]; then
|
||||
STAKING_ENABLED="false"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Parse buy bias parameter
|
||||
BUYBIAS_VALUE="50"
|
||||
if [[ $BUYBIAS == buybias=* ]]; then
|
||||
BUYBIAS_VALUE=${BUYBIAS#buybias=}
|
||||
fi
|
||||
|
||||
# Parse trades parameter
|
||||
TRADES_VALUE="15"
|
||||
if [[ $TRADES == trades=* ]]; then
|
||||
TRADES_VALUE=${TRADES#trades=}
|
||||
fi
|
||||
|
||||
# Parse staking bias parameter
|
||||
STAKINGBIAS_VALUE="80"
|
||||
if [[ $STAKINGBIAS == stakingbias=* ]]; then
|
||||
STAKINGBIAS_VALUE=${STAKINGBIAS#stakingbias=}
|
||||
fi
|
||||
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
OUTPUT_DIR="fuzzing_results_improved_${OPTIMIZER}_${TIMESTAMP}"
|
||||
|
||||
echo -e "${GREEN}=== Improved Fuzzing Analysis ===${NC}"
|
||||
echo "Optimizer: $OPTIMIZER"
|
||||
echo "Total runs: $RUNS_VALUE"
|
||||
echo "Trades per run: $TRADES_VALUE"
|
||||
echo "Staking: $([ "$STAKING_ENABLED" = "true" ] && echo "enabled" || echo "disabled")"
|
||||
if [ "$STAKING_ENABLED" = "true" ]; then
|
||||
echo "Staking bias: $STAKINGBIAS_VALUE%"
|
||||
fi
|
||||
echo "Buy bias: $BUYBIAS_VALUE%"
|
||||
echo "Output directory: $OUTPUT_DIR"
|
||||
echo ""
|
||||
|
||||
# Validate optimizer
|
||||
case $OPTIMIZER in
|
||||
BullMarketOptimizer|BearMarketOptimizer|NeutralMarketOptimizer|WhaleOptimizer|ExtremeOptimizer|MaliciousOptimizer)
|
||||
echo "Optimizer validation passed"
|
||||
;;
|
||||
*)
|
||||
echo -e "${RED}Error: Invalid optimizer class${NC}"
|
||||
echo "Valid options: BullMarketOptimizer, BearMarketOptimizer, NeutralMarketOptimizer, WhaleOptimizer, ExtremeOptimizer, MaliciousOptimizer"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create output directory
|
||||
mkdir -p $OUTPUT_DIR
|
||||
|
||||
# Run the improved fuzzing with buy bias
|
||||
echo -e "${YELLOW}Starting improved fuzzing analysis with buy bias...${NC}"
|
||||
echo ""
|
||||
|
||||
FUZZING_RUNS=$RUNS_VALUE \
|
||||
OPTIMIZER_CLASS=$OPTIMIZER \
|
||||
ENABLE_STAKING=$STAKING_ENABLED \
|
||||
BUY_BIAS=$BUYBIAS_VALUE \
|
||||
TRADES_PER_RUN=$TRADES_VALUE \
|
||||
STAKING_BIAS=$STAKINGBIAS_VALUE \
|
||||
TRACK_POSITIONS=true \
|
||||
forge script analysis/ImprovedFuzzingAnalysis.s.sol:ImprovedFuzzingAnalysis --gas-limit 100000000 -vv 2>&1 | tee $OUTPUT_DIR/fuzzing.log
|
||||
|
||||
# Extract key metrics
|
||||
echo ""
|
||||
echo -e "${GREEN}=== ANALYSIS COMPLETE ===${NC}"
|
||||
|
||||
# Show summary from log
|
||||
tail -20 $OUTPUT_DIR/fuzzing.log | grep -E "Total scenarios|Profitable|Discovery|Stakes|Snatches" || true
|
||||
|
||||
# Check for position CSVs
|
||||
POSITION_CSV_COUNT=$(ls -1 improved_positions_*.csv 2>/dev/null | wc -l)
|
||||
|
||||
if [ $POSITION_CSV_COUNT -gt 0 ]; then
|
||||
# Move position CSVs to output directory
|
||||
mv improved_positions_*.csv $OUTPUT_DIR/
|
||||
echo ""
|
||||
echo -e "${GREEN}Position CSVs saved to: $OUTPUT_DIR/${NC}"
|
||||
|
||||
# Calculate max staking percentage
|
||||
echo ""
|
||||
echo -e "${YELLOW}=== Maximum Staking Level ===${NC}"
|
||||
for f in $OUTPUT_DIR/improved_positions_*.csv; do
|
||||
tail -1 "$f" | cut -d',' -f13
|
||||
done | sort -n | tail -1 | awk '{
|
||||
pct = $1/1e16
|
||||
printf "%.2f%% of authorized stake (%.2f%% of KRAIKEN supply)\n", pct, pct*0.2
|
||||
}'
|
||||
fi
|
||||
|
||||
# Check for snatching activity
|
||||
echo ""
|
||||
echo -e "${YELLOW}=== Snatching Activity ===${NC}"
|
||||
SNATCH_COUNT=$(grep -c "SNATCHED" $OUTPUT_DIR/fuzzing.log 2>/dev/null || true)
|
||||
if [ -z "$SNATCH_COUNT" ]; then
|
||||
SNATCH_COUNT="0"
|
||||
fi
|
||||
if [ "$SNATCH_COUNT" -gt "0" ]; then
|
||||
echo -e "${GREEN}Snatching observed! Found $SNATCH_COUNT snatching events${NC}"
|
||||
grep "SNATCHED" $OUTPUT_DIR/fuzzing.log | head -5
|
||||
else
|
||||
echo "No snatching observed in this run"
|
||||
fi
|
||||
|
||||
# Check for profitable scenarios
|
||||
PROFITABLE_COUNT=$(grep -c "PROFITABLE!" $OUTPUT_DIR/fuzzing.log 2>/dev/null || true)
|
||||
if [ -z "$PROFITABLE_COUNT" ]; then
|
||||
PROFITABLE_COUNT="0"
|
||||
fi
|
||||
if [ "$PROFITABLE_COUNT" -gt "0" ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}=== PROFITABLE SCENARIOS FOUND ===${NC}"
|
||||
echo "Found $PROFITABLE_COUNT profitable scenarios"
|
||||
grep "PROFITABLE!" $OUTPUT_DIR/fuzzing.log | head -5
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Full results saved to: $OUTPUT_DIR/${NC}"
|
||||
echo ""
|
||||
echo "To view detailed logs:"
|
||||
echo " cat $OUTPUT_DIR/fuzzing.log"
|
||||
echo ""
|
||||
echo "To visualize position movements (if CSVs generated):"
|
||||
echo " ./analysis/view-scenarios.sh $OUTPUT_DIR"
|
||||
Loading…
Add table
Add a link
Reference in a new issue