package execution_test import ( "context" "fmt" "log/slog" "math/big" "os" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/your-org/mev-bot/pkg/arbitrage" "github.com/your-org/mev-bot/pkg/execution" mevtypes "github.com/your-org/mev-bot/pkg/types" ) // Example 1: Basic Execution Setup // Shows how to initialize the execution engine components func Example_basicSetup() { // Create logger logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, })) // Configure transaction builder builderConfig := execution.DefaultTransactionBuilderConfig() builderConfig.DefaultSlippageBPS = 50 // 0.5% builderConfig.MaxSlippageBPS = 300 // 3% chainID := big.NewInt(42161) // Arbitrum builder := execution.NewTransactionBuilder(builderConfig, chainID, logger) // Configure risk manager riskConfig := execution.DefaultRiskManagerConfig() riskConfig.MaxPositionSize = big.NewInt(10e18) // 10 ETH max riskConfig.MinProfitThreshold = big.NewInt(0.01e18) // 0.01 ETH min riskManager := execution.NewRiskManager(riskConfig, nil, logger) // Configure flashloan manager flashloanConfig := execution.DefaultFlashloanConfig() flashloanConfig.PreferredProviders = []execution.FlashloanProvider{ execution.FlashloanProviderAaveV3, execution.FlashloanProviderUniswapV3, } flashloanMgr := execution.NewFlashloanManager(flashloanConfig, logger) fmt.Printf("Transaction Builder: %v\n", builder != nil) fmt.Printf("Risk Manager: %v\n", riskManager != nil) fmt.Printf("Flashloan Manager: %v\n", flashloanMgr != nil) // Output: // Transaction Builder: true // Risk Manager: true // Flashloan Manager: true } // Example 2: Building a Simple Swap Transaction // Shows how to build a transaction for a single swap func Example_buildSimpleSwap() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) chainID := big.NewInt(42161) builder := execution.NewTransactionBuilder(nil, chainID, logger) // Create a simple arbitrage opportunity opp := &arbitrage.Opportunity{ ID: "simple-swap-1", Type: arbitrage.OpportunityTypeTwoPool, InputToken: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH InputAmount: big.NewInt(1e18), OutputToken: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), // USDC OutputAmount: big.NewInt(1500e6), Path: []arbitrage.SwapStep{ { Protocol: mevtypes.ProtocolUniswapV2, TokenIn: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), TokenOut: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), AmountIn: big.NewInt(1e18), AmountOut: big.NewInt(1500e6), PoolAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), }, }, EstimatedGas: 150000, } fromAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1") tx, err := builder.BuildTransaction(context.Background(), opp, fromAddress) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("Transaction built successfully\n") fmt.Printf("To: %s\n", tx.To.Hex()) fmt.Printf("Gas Limit: %d\n", tx.GasLimit) fmt.Printf("Slippage: %d bps\n", tx.Slippage) // Output: // Transaction built successfully // To: 0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506 // Gas Limit: 180000 // Slippage: 50 bps } // Example 3: Building a Multi-Hop Swap // Shows how to build a transaction for multiple swaps func Example_buildMultiHopSwap() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) chainID := big.NewInt(42161) builder := execution.NewTransactionBuilder(nil, chainID, logger) // Create a multi-hop opportunity opp := &arbitrage.Opportunity{ ID: "multihop-1", Type: arbitrage.OpportunityTypeMultiHop, InputToken: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), // WETH InputAmount: big.NewInt(1e18), OutputToken: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"), // WBTC OutputAmount: big.NewInt(1e7), Path: []arbitrage.SwapStep{ { Protocol: mevtypes.ProtocolUniswapV3, TokenIn: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), TokenOut: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), AmountIn: big.NewInt(1e18), AmountOut: big.NewInt(1500e6), PoolAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), Fee: 3000, }, { Protocol: mevtypes.ProtocolUniswapV3, TokenIn: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), TokenOut: common.HexToAddress("0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f"), AmountIn: big.NewInt(1500e6), AmountOut: big.NewInt(1e7), PoolAddress: common.HexToAddress("0x0000000000000000000000000000000000000002"), Fee: 500, }, }, EstimatedGas: 250000, } fromAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1") tx, err := builder.BuildTransaction(context.Background(), opp, fromAddress) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("Multi-hop transaction built\n") fmt.Printf("Steps: %d\n", len(opp.Path)) fmt.Printf("Gas Limit: %d\n", tx.GasLimit) // Output: // Multi-hop transaction built // Steps: 2 // Gas Limit: 300000 } // Example 4: Risk Assessment // Shows how to assess risk before execution func Example_riskAssessment() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) config := execution.DefaultRiskManagerConfig() config.MaxPositionSize = big.NewInt(10e18) config.MinProfitThreshold = big.NewInt(0.01e18) config.MinROI = 0.01 config.SimulationEnabled = false // Disable simulation for example riskManager := execution.NewRiskManager(config, nil, logger) opp := &arbitrage.Opportunity{ InputAmount: big.NewInt(5e18), // 5 ETH OutputAmount: big.NewInt(5.5e18), // 5.5 ETH NetProfit: big.NewInt(0.5e18), // 0.5 ETH profit ROI: 0.1, // 10% ROI EstimatedGas: 150000, } tx := &execution.SwapTransaction{ MaxFeePerGas: big.NewInt(50e9), // 50 gwei MaxPriorityFeePerGas: big.NewInt(2e9), // 2 gwei GasLimit: 180000, Slippage: 50, // 0.5% } assessment, err := riskManager.AssessRisk(context.Background(), opp, tx) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("Risk Assessment:\n") fmt.Printf("Approved: %v\n", assessment.Approved) fmt.Printf("Warnings: %d\n", len(assessment.Warnings)) if !assessment.Approved { fmt.Printf("Reason: %s\n", assessment.Reason) } // Output: // Risk Assessment: // Approved: true // Warnings: 0 } // Example 5: Flashloan Transaction // Shows how to build a flashloan-based arbitrage transaction func Example_buildFlashloanTransaction() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) config := execution.DefaultFlashloanConfig() config.ExecutorContract = common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1") flashloanMgr := execution.NewFlashloanManager(config, logger) opp := &arbitrage.Opportunity{ InputToken: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), InputAmount: big.NewInt(10e18), // 10 ETH Path: []arbitrage.SwapStep{ { TokenIn: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), TokenOut: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), PoolAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), }, }, } // Mock swap calldata swapCalldata := []byte{0x01, 0x02, 0x03, 0x04} flashTx, err := flashloanMgr.BuildFlashloanTransaction(context.Background(), opp, swapCalldata) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("Flashloan transaction built\n") fmt.Printf("Provider: %s\n", flashTx.Provider) fmt.Printf("Fee: %s wei\n", flashTx.Fee.String()) // Output: // Flashloan transaction built // Provider: aave_v3 // Fee: 9000000000000000 wei } // Example 6: Transaction Signing // Shows how to sign a transaction func Example_signTransaction() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) chainID := big.NewInt(42161) builder := execution.NewTransactionBuilder(nil, chainID, logger) // Generate a private key for testing privateKey, err := crypto.GenerateKey() if err != nil { fmt.Printf("Error generating key: %v\n", err) return } tx := &execution.SwapTransaction{ To: common.HexToAddress("0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506"), Data: []byte{0x01, 0x02, 0x03, 0x04}, Value: big.NewInt(0), GasLimit: 180000, MaxFeePerGas: big.NewInt(50e9), MaxPriorityFeePerGas: big.NewInt(2e9), } nonce := uint64(5) signedTx, err := builder.SignTransaction(tx, nonce, crypto.FromECDSA(privateKey)) if err != nil { fmt.Printf("Error signing: %v\n", err) return } fmt.Printf("Transaction signed\n") fmt.Printf("Nonce: %d\n", signedTx.Nonce()) fmt.Printf("Gas: %d\n", signedTx.Gas()) // Output: // Transaction signed // Nonce: 5 // Gas: 180000 } // Example 7: Custom Slippage Configuration // Shows how to configure custom slippage tolerance func Example_customSlippage() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) chainID := big.NewInt(42161) config := execution.DefaultTransactionBuilderConfig() config.DefaultSlippageBPS = 100 // 1% slippage config.MaxSlippageBPS = 500 // 5% max builder := execution.NewTransactionBuilder(config, chainID, logger) opp := &arbitrage.Opportunity{ ID: "custom-slippage-1", InputToken: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), InputAmount: big.NewInt(1e18), OutputToken: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), OutputAmount: big.NewInt(1000e6), Path: []arbitrage.SwapStep{ { Protocol: mevtypes.ProtocolUniswapV2, TokenIn: common.HexToAddress("0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"), TokenOut: common.HexToAddress("0xFF970a61A04b1cA14834A43f5dE4533eBDDB5CC8"), AmountIn: big.NewInt(1e18), AmountOut: big.NewInt(1000e6), PoolAddress: common.HexToAddress("0x0000000000000000000000000000000000000001"), }, }, EstimatedGas: 150000, } fromAddress := common.HexToAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb1") tx, err := builder.BuildTransaction(context.Background(), opp, fromAddress) if err != nil { fmt.Printf("Error: %v\n", err) return } // Calculate actual minimum output slippageFactor := float64(10000-tx.Slippage) / 10000.0 expectedMin := new(big.Float).Mul( new(big.Float).SetInt(opp.OutputAmount), big.NewFloat(slippageFactor), ) minOutputFloat, _ := expectedMin.Int(nil) fmt.Printf("Slippage: %d bps (%.2f%%)\n", tx.Slippage, float64(tx.Slippage)/100) fmt.Printf("Expected Output: %s\n", opp.OutputAmount.String()) fmt.Printf("Minimum Output: %s\n", minOutputFloat.String()) // Output: // Slippage: 100 bps (1.00%) // Expected Output: 1000000000 // Minimum Output: 990000000 } // Example 8: Circuit Breaker Pattern // Shows how the circuit breaker protects against cascading failures func Example_circuitBreaker() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) config := execution.DefaultRiskManagerConfig() config.CircuitBreakerFailures = 3 config.CircuitBreakerWindow = 1 * time.Minute config.CircuitBreakerCooldown = 5 * time.Minute config.SimulationEnabled = false riskManager := execution.NewRiskManager(config, nil, logger) // Simulate 3 failures for i := 0; i < 3; i++ { hash := common.HexToHash(fmt.Sprintf("0x%d", i)) riskManager.RecordFailure(hash, "test failure") } // Try to assess risk after failures opp := &arbitrage.Opportunity{ InputAmount: big.NewInt(1e18), OutputAmount: big.NewInt(1.1e18), NetProfit: big.NewInt(0.1e18), ROI: 0.1, EstimatedGas: 150000, } tx := &execution.SwapTransaction{ MaxFeePerGas: big.NewInt(50e9), MaxPriorityFeePerGas: big.NewInt(2e9), GasLimit: 180000, Slippage: 50, } assessment, _ := riskManager.AssessRisk(context.Background(), opp, tx) fmt.Printf("Circuit Breaker Status:\n") fmt.Printf("Approved: %v\n", assessment.Approved) if !assessment.Approved { fmt.Printf("Reason: Circuit breaker is open\n") } // Output: // Circuit Breaker Status: // Approved: false // Reason: Circuit breaker is open } // Example 9: Position Size Limits // Shows how position size limits protect capital func Example_positionSizeLimits() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) config := execution.DefaultRiskManagerConfig() config.MaxPositionSize = big.NewInt(5e18) // 5 ETH max config.SimulationEnabled = false riskManager := execution.NewRiskManager(config, nil, logger) // Try to execute with amount exceeding limit largeOpp := &arbitrage.Opportunity{ InputAmount: big.NewInt(10e18), // 10 ETH - exceeds limit OutputAmount: big.NewInt(11e18), NetProfit: big.NewInt(1e18), ROI: 0.1, EstimatedGas: 150000, } tx := &execution.SwapTransaction{ MaxFeePerGas: big.NewInt(50e9), MaxPriorityFeePerGas: big.NewInt(2e9), GasLimit: 180000, Slippage: 50, } assessment, _ := riskManager.AssessRisk(context.Background(), largeOpp, tx) fmt.Printf("Position Size Check:\n") fmt.Printf("Amount: 10 ETH\n") fmt.Printf("Limit: 5 ETH\n") fmt.Printf("Approved: %v\n", assessment.Approved) // Output: // Position Size Check: // Amount: 10 ETH // Limit: 5 ETH // Approved: false } // Example 10: Concurrent Transaction Management // Shows how the executor manages multiple pending transactions func Example_concurrentTransactions() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) config := execution.DefaultRiskManagerConfig() config.MaxConcurrentTxs = 3 config.SimulationEnabled = false riskManager := execution.NewRiskManager(config, nil, logger) // Track 3 concurrent transactions for i := 0; i < 3; i++ { hash := common.HexToHash(fmt.Sprintf("0x%d", i)) opp := &arbitrage.Opportunity{ InputAmount: big.NewInt(1e18), } gasPrice := big.NewInt(50e9) riskManager.TrackTransaction(hash, opp, gasPrice) } // Try to execute one more (should be rejected) opp := &arbitrage.Opportunity{ InputAmount: big.NewInt(1e18), OutputAmount: big.NewInt(1.1e18), NetProfit: big.NewInt(0.1e18), ROI: 0.1, EstimatedGas: 150000, } tx := &execution.SwapTransaction{ MaxFeePerGas: big.NewInt(50e9), MaxPriorityFeePerGas: big.NewInt(2e9), GasLimit: 180000, Slippage: 50, } assessment, _ := riskManager.AssessRisk(context.Background(), opp, tx) activeTxs := riskManager.GetActiveTransactions() fmt.Printf("Concurrent Transaction Management:\n") fmt.Printf("Active Transactions: %d\n", len(activeTxs)) fmt.Printf("Max Allowed: 3\n") fmt.Printf("New Transaction Approved: %v\n", assessment.Approved) // Output: // Concurrent Transaction Management: // Active Transactions: 3 // Max Allowed: 3 // New Transaction Approved: false } // Example 11: Gas Price Strategy // Shows different gas price strategies func Example_gasPriceStrategy() { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) strategies := []struct { name string strategy string multiplier float64 }{ {"Fast", "fast", 1.2}, {"Market", "market", 1.0}, {"Aggressive", "aggressive", 1.5}, } for _, s := range strategies { config := &execution.ExecutorConfig{ GasPriceStrategy: s.strategy, GasPriceMultiplier: s.multiplier, } fmt.Printf("%s Strategy:\n", s.name) fmt.Printf(" Multiplier: %.1fx\n", config.GasPriceMultiplier) fmt.Printf(" Use Case: ") switch s.strategy { case "fast": fmt.Printf("Quick execution, moderate cost\n") case "market": fmt.Printf("Market rate, standard execution\n") case "aggressive": fmt.Printf("Priority execution, higher cost\n") } } // Output: // Fast Strategy: // Multiplier: 1.2x // Use Case: Quick execution, moderate cost // Market Strategy: // Multiplier: 1.0x // Use Case: Market rate, standard execution // Aggressive Strategy: // Multiplier: 1.5x // Use Case: Priority execution, higher cost }