feat(core): implement core MEV bot functionality with market scanning and Uniswap V3 pricing

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Krypto Kajun
2025-09-14 10:16:29 -05:00
parent 5db7587923
commit c16182d80c
1364 changed files with 473970 additions and 1202 deletions

View File

@@ -7,16 +7,16 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/fraktal/mev-beta/internal/config"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/internal/ratelimit"
"github.com/fraktal/mev-beta/pkg/arbitrum"
"github.com/fraktal/mev-beta/pkg/market"
"github.com/fraktal/mev-beta/pkg/scanner"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"golang.org/x/time/rate"
)
@@ -67,7 +67,7 @@ func NewArbitrumMonitor(
// Create pipeline
pipeline := market.NewPipeline(botCfg, logger, marketMgr, scanner, client)
// Add default stages
pipeline.AddDefaultStages()
@@ -194,16 +194,16 @@ func (m *ArbitrumMonitor) processBlock(ctx context.Context, blockNumber uint64)
// Parse DEX transactions from the block
dexTransactions := m.l2Parser.ParseDEXTransactions(ctx, l2Block)
m.logger.Info(fmt.Sprintf("Block %d: Processing %d transactions, found %d DEX transactions",
m.logger.Info(fmt.Sprintf("Block %d: Processing %d transactions, found %d DEX transactions",
blockNumber, len(l2Block.Transactions), len(dexTransactions)))
// Process DEX transactions
if len(dexTransactions) > 0 {
m.logger.Info(fmt.Sprintf("Block %d contains %d DEX transactions:", blockNumber, len(dexTransactions)))
for i, dexTx := range dexTransactions {
m.logger.Info(fmt.Sprintf(" [%d] %s: %s -> %s (%s) calling %s (%s)",
i+1, dexTx.Hash, dexTx.From, dexTx.To, dexTx.ContractName,
m.logger.Info(fmt.Sprintf(" [%d] %s: %s -> %s (%s) calling %s (%s)",
i+1, dexTx.Hash, dexTx.From, dexTx.To, dexTx.ContractName,
dexTx.FunctionName, dexTx.Protocol))
}
@@ -216,7 +216,7 @@ func (m *ArbitrumMonitor) processBlock(ctx context.Context, blockNumber uint64)
if len(l2Block.Transactions) == 0 {
m.logger.Info(fmt.Sprintf("Block %d: Empty block", blockNumber))
} else {
m.logger.Info(fmt.Sprintf("Block %d: No DEX transactions found in %d total transactions",
m.logger.Info(fmt.Sprintf("Block %d: No DEX transactions found in %d total transactions",
blockNumber, len(l2Block.Transactions)))
}
}
@@ -233,25 +233,25 @@ func (m *ArbitrumMonitor) subscribeToDEXEvents(ctx context.Context) error {
}{
// Official Arbitrum DEX Factories
{common.HexToAddress("0xf1D7CC64Fb4452F05c498126312eBE29f30Fbcf9"), "UniswapV2Factory"}, // Official Uniswap V2 Factory
{common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), "UniswapV3Factory"}, // Official Uniswap V3 Factory
{common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"), "UniswapV3Factory"}, // Official Uniswap V3 Factory
{common.HexToAddress("0xc35DADB65012eC5796536bD9864eD8773aBc74C4"), "SushiSwapFactory"}, // Official SushiSwap V2 Factory
// Official Arbitrum DEX Routers
{common.HexToAddress("0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24"), "UniswapV2Router02"}, // Official Uniswap V2 Router02
{common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"), "UniswapV3Router"}, // Official Uniswap V3 SwapRouter
{common.HexToAddress("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"), "UniswapV3Router02"}, // Official Uniswap V3 SwapRouter02
{common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"), "SushiSwapRouter"}, // Official SushiSwap Router
// Official Arbitrum DEX Routers
{common.HexToAddress("0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24"), "UniswapV2Router02"}, // Official Uniswap V2 Router02
{common.HexToAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"), "UniswapV3Router"}, // Official Uniswap V3 SwapRouter
{common.HexToAddress("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"), "UniswapV3Router02"}, // Official Uniswap V3 SwapRouter02
{common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"), "SushiSwapRouter"}, // Official SushiSwap Router
{common.HexToAddress("0xC36442b4a4522E871399CD717aBDD847Ab11FE88"), "UniswapV3PositionManager"}, // Official Position Manager
// Additional official routers
{common.HexToAddress("0xa51afafe0263b40edaef0df8781ea9aa03e381a3"), "UniversalRouter"}, // Universal Router
{common.HexToAddress("0x4C60051384bd2d3C01bfc845Cf5F4b44bcbE9de5"), "GMX Router"}, // GMX DEX Router
{common.HexToAddress("0x4C60051384bd2d3C01bfc845Cf5F4b44bcbE9de5"), "GMX Router"}, // GMX DEX Router
// Popular Arbitrum pools (verified high volume pools)
{common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"), "USDC/WETH UniswapV3 0.05%"}, // High volume pool
{common.HexToAddress("0x17c14D2c404D167802b16C450d3c99F88F2c4F4d"), "USDC/WETH UniswapV3 0.3%"}, // High volume pool
{common.HexToAddress("0x2f5e87C9312fa29aed5c179E456625D79015299c"), "WBTC/WETH UniswapV3 0.05%"}, // High volume pool
{common.HexToAddress("0x149e36E72726e0BceA5c59d40df2c43F60f5A22D"), "WBTC/WETH UniswapV3 0.3%"}, // High volume pool
{common.HexToAddress("0x149e36E72726e0BceA5c59d40df2c43F60f5A22D"), "WBTC/WETH UniswapV3 0.3%"}, // High volume pool
{common.HexToAddress("0x641C00A822e8b671738d32a431a4Fb6074E5c79d"), "USDT/WETH UniswapV3 0.05%"}, // High volume pool
{common.HexToAddress("0xFe7D6a84287235C7b4b57C4fEb9a44d4C6Ed3BB8"), "ARB/WETH UniswapV3 0.05%"}, // ARB native token pool
}
@@ -286,7 +286,7 @@ func (m *ArbitrumMonitor) subscribeToDEXEvents(ctx context.Context) error {
if err != nil {
return fmt.Errorf("failed to subscribe to DEX events: %v", err)
}
m.logger.Info("Subscribed to DEX events")
// Process logs in a goroutine
@@ -296,7 +296,7 @@ func (m *ArbitrumMonitor) subscribeToDEXEvents(ctx context.Context) error {
m.logger.Error(fmt.Sprintf("Panic in DEX event processor: %v", r))
}
}()
defer sub.Unsubscribe()
for {
@@ -328,25 +328,25 @@ func (m *ArbitrumMonitor) processDEXEvent(ctx context.Context, log types.Log) {
// Check for common swap event signatures
switch eventSig.Hex() {
case "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822": // Uniswap V2 Swap
m.logger.Info(fmt.Sprintf("Uniswap V2 Swap event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V2 Swap event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
case "0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67": // Uniswap V3 Swap
m.logger.Info(fmt.Sprintf("Uniswap V3 Swap event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V3 Swap event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
case "0x4c209b5fc8ad50758f13e2e1088ba56a560dff690a1c6fef26394f4c03821c4f": // Uniswap V2 Mint
m.logger.Info(fmt.Sprintf("Uniswap V2 Mint event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V2 Mint event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
case "0x7a53080ba414158be7ec69b987b5fb7d07dee101fe85488f0853ae16239d0bde": // Uniswap V2 Burn
m.logger.Info(fmt.Sprintf("Uniswap V2 Burn event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V2 Burn event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
case "0x783cca1c0412dd0d695e784568c96da2e9c22ff989357a2e8b1d9b2b4e6b7118": // Uniswap V3 Mint
m.logger.Info(fmt.Sprintf("Uniswap V3 Mint event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V3 Mint event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
case "0x0c396cd989a39f49a56c8a608a0409f2075c6b60e9c44533b5cf87abdbe393f1": // Uniswap V3 Burn
m.logger.Info(fmt.Sprintf("Uniswap V3 Burn event detected: Contract=%s, TxHash=%s",
m.logger.Info(fmt.Sprintf("Uniswap V3 Burn event detected: Contract=%s, TxHash=%s",
log.Address.Hex(), log.TxHash.Hex()))
default:
m.logger.Debug(fmt.Sprintf("Other DEX event detected: Contract=%s, EventSig=%s, TxHash=%s",
m.logger.Debug(fmt.Sprintf("Other DEX event detected: Contract=%s, EventSig=%s, TxHash=%s",
log.Address.Hex(), eventSig.Hex(), log.TxHash.Hex()))
}
@@ -369,7 +369,7 @@ func (m *ArbitrumMonitor) processTransactionReceipt(ctx context.Context, receipt
return
}
m.logger.Debug(fmt.Sprintf("Processing transaction receipt %s from block %d",
m.logger.Debug(fmt.Sprintf("Processing transaction receipt %s from block %d",
receipt.TxHash.Hex(), blockNumber))
// Process transaction logs for DEX events
@@ -377,7 +377,7 @@ func (m *ArbitrumMonitor) processTransactionReceipt(ctx context.Context, receipt
for _, log := range receipt.Logs {
if len(log.Topics) > 0 {
eventSig := log.Topics[0]
// Check for common DEX event signatures
switch eventSig.Hex() {
case "0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822": // Uniswap V2 Swap
@@ -401,11 +401,11 @@ func (m *ArbitrumMonitor) processTransactionReceipt(ctx context.Context, receipt
}
}
}
if dexEvents > 0 {
m.logger.Info(fmt.Sprintf("Transaction %s contains %d DEX events", receipt.TxHash.Hex(), dexEvents))
}
// Create a minimal transaction for the pipeline
// This is just a stub since we don't have the full transaction data
tx := types.NewTransaction(0, common.Address{}, big.NewInt(0), 0, big.NewInt(0), nil)
@@ -469,25 +469,25 @@ func (m *ArbitrumMonitor) GetPendingTransactions(ctx context.Context) ([]*types.
func (m *ArbitrumMonitor) getTransactionReceiptWithRetry(ctx context.Context, txHash common.Hash, maxRetries int) (*types.Receipt, error) {
for attempt := 0; attempt < maxRetries; attempt++ {
m.logger.Debug(fmt.Sprintf("Attempting to fetch receipt for transaction %s (attempt %d/%d)", txHash.Hex(), attempt+1, maxRetries))
// Try to fetch the transaction receipt
receipt, err := m.client.TransactionReceipt(ctx, txHash)
if err == nil {
m.logger.Debug(fmt.Sprintf("Successfully fetched receipt for transaction %s on attempt %d", txHash.Hex(), attempt+1))
return receipt, nil
}
// Check for specific error types that shouldn't be retried
if ctx.Err() != nil {
return nil, ctx.Err()
}
// Log retry attempt for other errors
if attempt < maxRetries-1 {
backoffDuration := time.Duration(1<<uint(attempt)) * time.Second
m.logger.Warn(fmt.Sprintf("Receipt fetch for transaction %s attempt %d failed: %v, retrying in %v",
m.logger.Warn(fmt.Sprintf("Receipt fetch for transaction %s attempt %d failed: %v, retrying in %v",
txHash.Hex(), attempt+1, err, backoffDuration))
select {
case <-ctx.Done():
return nil, ctx.Err()
@@ -498,6 +498,6 @@ func (m *ArbitrumMonitor) getTransactionReceiptWithRetry(ctx context.Context, tx
m.logger.Error(fmt.Sprintf("Receipt fetch for transaction %s failed after %d attempts: %v", txHash.Hex(), maxRetries, err))
}
}
return nil, fmt.Errorf("failed to fetch receipt for transaction %s after %d attempts", txHash.Hex(), maxRetries)
}
}