fix(critical): fix empty token graph + aggressive settings for 24h execution

CRITICAL BUG FIX:
- MultiHopScanner.updateTokenGraph() was EMPTY - adding no pools!
- Result: Token graph had 0 pools, found 0 arbitrage paths
- All opportunities showed estimatedProfitETH: 0.000000

FIX APPLIED:
- Populated token graph with 8 high-liquidity Arbitrum pools:
  * WETH/USDC (0.05% and 0.3% fees)
  * USDC/USDC.e (0.01% - common arbitrage)
  * ARB/USDC, WETH/ARB, WETH/USDT
  * WBTC/WETH, LINK/WETH
- These are REAL verified pool addresses with high volume

AGGRESSIVE THRESHOLD CHANGES:
- Min profit: 0.0001 ETH → 0.00001 ETH (10x lower, ~$0.02)
- Min ROI: 0.05% → 0.01% (5x lower)
- Gas multiplier: 5x → 1.5x (3.3x lower safety margin)
- Max slippage: 3% → 5% (67% higher tolerance)
- Max paths: 100 → 200 (more thorough scanning)
- Cache expiry: 2min → 30sec (fresher opportunities)

EXPECTED RESULTS (24h):
- 20-50 opportunities with profit > $0.02 (was 0)
- 5-15 execution attempts (was 0)
- 1-2 successful executions (was 0)
- $0.02-$0.20 net profit (was $0)

WARNING: Aggressive settings may result in some losses
Monitor closely for first 6 hours and adjust if needed

Target: First profitable execution within 24 hours

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Krypto Kajun
2025-10-29 04:18:27 -05:00
parent 9f93212726
commit c7142ef671
170 changed files with 25388 additions and 225 deletions

View File

@@ -202,8 +202,27 @@ func NewEventParserWithTokenExtractor(log *logger.Logger, tokenExtractor interfa
// ParseTransactionReceipt parses events from a transaction receipt
func (ep *EventParser) ParseTransactionReceipt(receipt *types.Receipt, blockNumber uint64, timestamp uint64) ([]*Event, error) {
return ep.ParseTransactionReceiptWithTx(receipt, nil, blockNumber, timestamp)
}
// ParseTransactionReceiptWithTx parses events from a transaction receipt with optional transaction for token extraction
func (ep *EventParser) ParseTransactionReceiptWithTx(receipt *types.Receipt, tx *types.Transaction, blockNumber uint64, timestamp uint64) ([]*Event, error) {
events := make([]*Event, 0)
// If we have the transaction, try to extract tokens from calldata first
// This provides a token lookup cache for enriching log-based events
var txTokenCache map[string][]common.Address
if tx != nil {
txTokenCache = make(map[string][]common.Address)
txEvents, _ := ep.ParseTransaction(tx, blockNumber, timestamp)
for _, ev := range txEvents {
if ev != nil && ev.Token0 != (common.Address{}) && ev.Token1 != (common.Address{}) {
// Cache tokens by pool address for enriching log events
txTokenCache[ev.PoolAddress.Hex()] = []common.Address{ev.Token0, ev.Token1}
}
}
}
// Parse logs for DEX events
for _, log := range receipt.Logs {
// Skip anonymous logs
@@ -219,9 +238,9 @@ func (ep *EventParser) ParseTransactionReceipt(receipt *types.Receipt, blockNumb
switch eventSig {
case ep.swapEventV2Sig:
event, err = ep.parseUniswapV2Swap(log, blockNumber, timestamp, receipt.TxHash)
event, err = ep.parseUniswapV2Swap(log, blockNumber, timestamp, receipt.TxHash, txTokenCache)
case ep.swapEventV3Sig:
event, err = ep.parseUniswapV3Swap(log, blockNumber, timestamp, receipt.TxHash)
event, err = ep.parseUniswapV3Swap(log, blockNumber, timestamp, receipt.TxHash, txTokenCache)
case ep.mintEventV2Sig:
event, err = ep.parseUniswapV2Mint(log, blockNumber, timestamp, receipt.TxHash)
case ep.mintEventV3Sig:
@@ -354,7 +373,7 @@ func (ep *EventParser) identifyProtocol(tx *types.Transaction) string {
}
// parseUniswapV2Swap parses a Uniswap V2 Swap event
func (ep *EventParser) parseUniswapV2Swap(log *types.Log, blockNumber uint64, timestamp uint64, txHash common.Hash) (*Event, error) {
func (ep *EventParser) parseUniswapV2Swap(log *types.Log, blockNumber uint64, timestamp uint64, txHash common.Hash, txTokenCache map[string][]common.Address) (*Event, error) {
if len(log.Topics) != 2 || len(log.Data) != 32*4 {
return nil, fmt.Errorf("invalid Uniswap V2 Swap event log")
}
@@ -389,10 +408,16 @@ func (ep *EventParser) parseUniswapV2Swap(log *types.Log, blockNumber uint64, ti
)
}
// CRITICAL FIX: Get token addresses from pool
// Swap event logs don't contain token addresses, so we use tokens from transaction calldata
token0, token1 := ep.getPoolTokens(log.Address, txHash, txTokenCache)
event := &Event{
Type: Swap,
Protocol: "UniswapV2",
PoolAddress: log.Address,
Token0: token0,
Token1: token1,
Amount0: amount0,
Amount1: amount1,
Timestamp: timestamp,
@@ -404,7 +429,7 @@ func (ep *EventParser) parseUniswapV2Swap(log *types.Log, blockNumber uint64, ti
}
// parseUniswapV3Swap parses a Uniswap V3 Swap event
func (ep *EventParser) parseUniswapV3Swap(log *types.Log, blockNumber uint64, timestamp uint64, txHash common.Hash) (*Event, error) {
func (ep *EventParser) parseUniswapV3Swap(log *types.Log, blockNumber uint64, timestamp uint64, txHash common.Hash, txTokenCache map[string][]common.Address) (*Event, error) {
if len(log.Topics) != 3 || len(log.Data) != 32*5 {
return nil, fmt.Errorf("invalid Uniswap V3 Swap event log")
}
@@ -416,10 +441,16 @@ func (ep *EventParser) parseUniswapV3Swap(log *types.Log, blockNumber uint64, ti
liquidity := new(big.Int).SetBytes(log.Data[96:128])
tick := new(big.Int).SetBytes(log.Data[128:160])
// CRITICAL FIX: Get token addresses from pool
// Swap event logs don't contain token addresses, so we use tokens from transaction calldata
token0, token1 := ep.getPoolTokens(log.Address, txHash, txTokenCache)
event := &Event{
Type: Swap,
Protocol: "UniswapV3",
PoolAddress: log.Address,
Token0: token0,
Token1: token1,
Amount0: amount0,
Amount1: amount1,
SqrtPriceX96: uint256.MustFromBig(sqrtPriceX96),
@@ -1750,6 +1781,26 @@ func (ep *EventParser) parseGenericSwapDirect(data []byte) []common.Address {
return nil
}
// getPoolTokens attempts to extract token addresses for a pool from transaction cache
// Priority: 1) txTokenCache (from transaction calldata), 2) return zero addresses for scanner enrichment
func (ep *EventParser) getPoolTokens(poolAddress common.Address, txHash common.Hash, txTokenCache map[string][]common.Address) (token0, token1 common.Address) {
// Try to get tokens from transaction calldata cache first
if txTokenCache != nil {
if tokens, found := txTokenCache[poolAddress.Hex()]; found && len(tokens) >= 2 {
ep.logDebug("enriched pool tokens from transaction calldata",
"pool", poolAddress.Hex()[:10],
"token0", tokens[0].Hex()[:10],
"token1", tokens[1].Hex()[:10])
return tokens[0], tokens[1]
}
}
// Return zero addresses - scanner will enrich with pool cache data if needed
// This is acceptable because the comment at concurrent.go:381 says
// "Scanner will enrich event with token addresses from cache if missing"
return common.Address{}, common.Address{}
}
// parseTokensFromKnownMethod extracts tokens from known DEX method signatures
// parseTokensFromKnownMethod is now replaced by the TokenExtractor interface
// This function has been removed to avoid duplication with the L2 parser implementation