refactor: move all remaining files to orig/ directory
Completed clean root directory structure: - Root now contains only: .git, .env, docs/, orig/ - Moved all remaining files and directories to orig/: - Config files (.claude, .dockerignore, .drone.yml, etc.) - All .env variants (except active .env) - Git config (.gitconfig, .github, .gitignore, etc.) - Tool configs (.golangci.yml, .revive.toml, etc.) - Documentation (*.md files, @prompts) - Build files (Dockerfiles, Makefile, go.mod, go.sum) - Docker compose files - All source directories (scripts, tests, tools, etc.) - Runtime directories (logs, monitoring, reports) - Dependency files (node_modules, lib, cache) - Special files (--delete) - Removed empty runtime directories (bin/, data/) V2 structure is now clean: - docs/planning/ - V2 planning documents - orig/ - Complete V1 codebase preserved - .env - Active environment config (not in git) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
565
orig/test/integration/real_world_profitability_test.go
Normal file
565
orig/test/integration/real_world_profitability_test.go
Normal file
@@ -0,0 +1,565 @@
|
||||
//go:build integration && legacy && forked
|
||||
// +build integration,legacy,forked
|
||||
|
||||
package integration_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/fraktal/mev-beta/internal/logger"
|
||||
"github.com/fraktal/mev-beta/pkg/mev"
|
||||
"github.com/fraktal/mev-beta/pkg/security"
|
||||
"github.com/fraktal/mev-beta/pkg/types"
|
||||
)
|
||||
|
||||
// TestRealWorldProfitability tests actual profitability with real market conditions
|
||||
func TestRealWorldProfitability(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping real-world profitability test in short mode")
|
||||
}
|
||||
|
||||
// Set up real environment
|
||||
setupRealEnvironment(t)
|
||||
|
||||
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
|
||||
require.NoError(t, err, "Failed to connect to Arbitrum")
|
||||
defer client.Close()
|
||||
|
||||
log := logger.New("debug", "text", "")
|
||||
|
||||
t.Run("TestActualArbitrageOpportunityDetection", func(t *testing.T) {
|
||||
// Test with real WETH/USDC pool on Arbitrum
|
||||
wethUsdcPool := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443")
|
||||
|
||||
// Query real pool state
|
||||
opportunities, err := detectRealArbitrageOpportunities(client, wethUsdcPool, log)
|
||||
require.NoError(t, err)
|
||||
|
||||
if len(opportunities) > 0 {
|
||||
t.Logf("✅ Found %d real arbitrage opportunities", len(opportunities))
|
||||
|
||||
for i, opp := range opportunities {
|
||||
t.Logf("Opportunity %d: Profit=%s ETH, Gas=%s, ROI=%.2f%%",
|
||||
i+1, formatEther(opp.Profit), opp.GasEstimate.String(), opp.ROI)
|
||||
|
||||
// Validate minimum profitability
|
||||
assert.True(t, opp.Profit.Cmp(big.NewInt(50000000000000000)) >= 0, // 0.05 ETH min
|
||||
"Opportunity should meet minimum profit threshold")
|
||||
}
|
||||
} else {
|
||||
t.Log("⚠️ No arbitrage opportunities found at this time (normal)")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestRealGasCostCalculation", func(t *testing.T) {
|
||||
// Get real gas prices from Arbitrum
|
||||
gasPrice, err := client.SuggestGasPrice(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Current Arbitrum gas price: %s gwei", formatGwei(gasPrice))
|
||||
|
||||
// Test realistic arbitrage gas costs
|
||||
baseGas := uint64(800000) // 800k gas for flash swap arbitrage
|
||||
totalCost := new(big.Int).Mul(gasPrice, big.NewInt(int64(baseGas)))
|
||||
|
||||
// Add MEV premium (15x competitive)
|
||||
mevPremium := big.NewInt(15)
|
||||
competitiveCost := new(big.Int).Mul(totalCost, mevPremium)
|
||||
|
||||
t.Logf("Base gas cost: %s ETH", formatEther(totalCost))
|
||||
t.Logf("Competitive MEV cost: %s ETH", formatEther(competitiveCost))
|
||||
|
||||
// Validate cost is reasonable for arbitrage
|
||||
maxReasonableCost := big.NewInt(100000000000000000) // 0.1 ETH max
|
||||
assert.True(t, competitiveCost.Cmp(maxReasonableCost) <= 0,
|
||||
"MEV gas cost should be reasonable for profitable arbitrage")
|
||||
})
|
||||
|
||||
t.Run("TestMEVCompetitionAnalysis", func(t *testing.T) {
|
||||
analyzer := mev.NewCompetitionAnalyzer(client, log)
|
||||
|
||||
// Create realistic MEV opportunity
|
||||
opportunity := &mev.MEVOpportunity{
|
||||
OpportunityType: "arbitrage",
|
||||
EstimatedProfit: big.NewInt(200000000000000000), // 0.2 ETH
|
||||
RequiredGas: 800000,
|
||||
}
|
||||
|
||||
// Analyze real competition
|
||||
competition, err := analyzer.AnalyzeCompetition(context.Background(), opportunity)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Competition analysis:")
|
||||
t.Logf(" Competing bots: %d", competition.CompetingBots)
|
||||
t.Logf(" Competition intensity: %.2f", competition.CompetitionIntensity)
|
||||
t.Logf(" Highest priority fee: %s gwei", formatGwei(competition.HighestPriorityFee))
|
||||
|
||||
// Calculate optimal bid
|
||||
bidStrategy, err := analyzer.CalculateOptimalBid(context.Background(), opportunity, competition)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Optimal bidding strategy:")
|
||||
t.Logf(" Priority fee: %s gwei", formatGwei(bidStrategy.PriorityFee))
|
||||
t.Logf(" Total cost: %s ETH", formatEther(bidStrategy.TotalCost))
|
||||
t.Logf(" Success probability: %.1f%%", bidStrategy.SuccessProbability*100)
|
||||
|
||||
// Validate profitability after competitive bidding
|
||||
netProfit := new(big.Int).Sub(opportunity.EstimatedProfit, bidStrategy.TotalCost)
|
||||
assert.True(t, netProfit.Sign() > 0, "Should remain profitable after competitive bidding")
|
||||
|
||||
t.Logf("✅ Net profit after competition: %s ETH", formatEther(netProfit))
|
||||
})
|
||||
}
|
||||
|
||||
// TestRealContractInteraction tests interaction with real Arbitrum contracts
|
||||
func TestRealContractInteraction(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping real contract interaction test in short mode")
|
||||
}
|
||||
|
||||
setupRealEnvironment(t)
|
||||
|
||||
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
|
||||
require.NoError(t, err)
|
||||
defer client.Close()
|
||||
|
||||
t.Run("TestUniswapV3PoolQuery", func(t *testing.T) {
|
||||
// Test real Uniswap V3 WETH/USDC pool
|
||||
poolAddress := common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443")
|
||||
|
||||
// Query pool state
|
||||
poolData, err := queryUniswapV3Pool(client, poolAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("WETH/USDC Pool State:")
|
||||
t.Logf(" Token0: %s", poolData.Token0.Hex())
|
||||
t.Logf(" Token1: %s", poolData.Token1.Hex())
|
||||
t.Logf(" Fee: %d", poolData.Fee)
|
||||
t.Logf(" Liquidity: %s", poolData.Liquidity.String())
|
||||
t.Logf(" Current Price: %s", poolData.Price.String())
|
||||
|
||||
// Validate pool data
|
||||
assert.NotEqual(t, common.Address{}, poolData.Token0, "Token0 should be valid")
|
||||
assert.NotEqual(t, common.Address{}, poolData.Token1, "Token1 should be valid")
|
||||
assert.True(t, poolData.Liquidity.Sign() > 0, "Pool should have liquidity")
|
||||
})
|
||||
|
||||
t.Run("TestCamelotRouterQuery", func(t *testing.T) {
|
||||
// Test real Camelot router
|
||||
routerAddress := common.HexToAddress("0xc873fEcbd354f5A56E00E710B90EF4201db2448d")
|
||||
|
||||
// Query price for WETH -> USDC swap
|
||||
weth := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
||||
usdc := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
||||
|
||||
price, err := queryCamelotPrice(client, routerAddress, weth, usdc, big.NewInt(1000000000000000000)) // 1 WETH
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Camelot WETH->USDC price: %s USDC for 1 WETH", price.String())
|
||||
assert.True(t, price.Sign() > 0, "Should get positive USDC amount for WETH")
|
||||
})
|
||||
|
||||
t.Run("TestTokenBalanceQuery", func(t *testing.T) {
|
||||
// Test querying real token balances
|
||||
wethAddress := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
||||
|
||||
// Query WETH total supply (should be very large)
|
||||
totalSupply, err := queryTokenSupply(client, wethAddress)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("WETH total supply: %s", totalSupply.String())
|
||||
assert.True(t, totalSupply.Cmp(big.NewInt(1000000000000000000)) > 0, // > 1 WETH
|
||||
"WETH should have significant total supply")
|
||||
})
|
||||
}
|
||||
|
||||
// TestProfitabilityUnderLoad tests profitability under realistic load
|
||||
func TestProfitabilityUnderLoad(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping load test in short mode")
|
||||
}
|
||||
|
||||
setupRealEnvironment(t)
|
||||
|
||||
client, err := ethclient.Dial(os.Getenv("ARBITRUM_RPC_ENDPOINT"))
|
||||
require.NoError(t, err)
|
||||
defer client.Close()
|
||||
|
||||
log := logger.New("info", "text", "")
|
||||
|
||||
t.Run("TestConcurrentOpportunityDetection", func(t *testing.T) {
|
||||
// Test detecting opportunities concurrently (realistic scenario)
|
||||
numWorkers := 5
|
||||
opportunities := make(chan *types.ArbitrageOpportunity, 100)
|
||||
|
||||
// Start workers to detect opportunities
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
go func(workerID int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("Worker %d panicked: %v", workerID, r)
|
||||
}
|
||||
}()
|
||||
|
||||
for j := 0; j < 10; j++ { // Each worker checks 10 times
|
||||
opps, err := detectRealArbitrageOpportunities(client,
|
||||
common.HexToAddress("0xC31E54c7a869B9FcBEcc14363CF510d1c41fa443"), log)
|
||||
if err == nil {
|
||||
for _, opp := range opps {
|
||||
select {
|
||||
case opportunities <- opp:
|
||||
default:
|
||||
// Channel full, skip
|
||||
}
|
||||
}
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
// Collect results for 5 seconds
|
||||
timeout := time.After(5 * time.Second)
|
||||
var totalOpportunities int
|
||||
var totalPotentialProfit *big.Int = big.NewInt(0)
|
||||
|
||||
collectLoop:
|
||||
for {
|
||||
select {
|
||||
case opp := <-opportunities:
|
||||
totalOpportunities++
|
||||
totalPotentialProfit.Add(totalPotentialProfit, opp.Profit)
|
||||
case <-timeout:
|
||||
break collectLoop
|
||||
}
|
||||
}
|
||||
|
||||
t.Logf("Load test results:")
|
||||
t.Logf(" Total opportunities detected: %d", totalOpportunities)
|
||||
t.Logf(" Total potential profit: %s ETH", formatEther(totalPotentialProfit))
|
||||
|
||||
if totalOpportunities > 0 {
|
||||
avgProfit := new(big.Int).Div(totalPotentialProfit, big.NewInt(int64(totalOpportunities)))
|
||||
t.Logf(" Average profit per opportunity: %s ETH", formatEther(avgProfit))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestGasCostVariability", func(t *testing.T) {
|
||||
// Test gas cost variations over time
|
||||
var gasPrices []*big.Int
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
gasPrice, err := client.SuggestGasPrice(context.Background())
|
||||
if err == nil {
|
||||
gasPrices = append(gasPrices, gasPrice)
|
||||
}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
if len(gasPrices) > 0 {
|
||||
var total *big.Int = big.NewInt(0)
|
||||
var min, max *big.Int = gasPrices[0], gasPrices[0]
|
||||
|
||||
for _, price := range gasPrices {
|
||||
total.Add(total, price)
|
||||
if price.Cmp(min) < 0 {
|
||||
min = price
|
||||
}
|
||||
if price.Cmp(max) > 0 {
|
||||
max = price
|
||||
}
|
||||
}
|
||||
|
||||
avg := new(big.Int).Div(total, big.NewInt(int64(len(gasPrices))))
|
||||
|
||||
t.Logf("Gas price variability:")
|
||||
t.Logf(" Min: %s gwei", formatGwei(min))
|
||||
t.Logf(" Max: %s gwei", formatGwei(max))
|
||||
t.Logf(" Avg: %s gwei", formatGwei(avg))
|
||||
|
||||
// Validate gas prices are in reasonable range for Arbitrum
|
||||
maxReasonable := big.NewInt(10000000000) // 10 gwei
|
||||
assert.True(t, max.Cmp(maxReasonable) <= 0, "Gas prices should be reasonable for Arbitrum")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestSecurityUnderAttack tests security under realistic attack scenarios
|
||||
func TestSecurityUnderAttack(t *testing.T) {
|
||||
setupRealEnvironment(t)
|
||||
|
||||
t.Run("TestInvalidRPCEndpoints", func(t *testing.T) {
|
||||
maliciousEndpoints := []string{
|
||||
"http://malicious-rpc.evil.com",
|
||||
"https://fake-arbitrum.scam.org",
|
||||
"ws://localhost:1337", // Without localhost override
|
||||
"ftp://invalid-scheme.com",
|
||||
"",
|
||||
}
|
||||
|
||||
for _, endpoint := range maliciousEndpoints {
|
||||
err := validateRPCEndpoint(endpoint)
|
||||
assert.Error(t, err, "Should reject malicious endpoint: %s", endpoint)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestKeyManagerSecurity", func(t *testing.T) {
|
||||
// Test with various encryption key scenarios
|
||||
testCases := []struct {
|
||||
name string
|
||||
encryptionKey string
|
||||
shouldFail bool
|
||||
}{
|
||||
{"Empty key", "", true},
|
||||
{"Short key", "short", true},
|
||||
{"Weak key", "password123", true},
|
||||
{"Strong key", "very-secure-encryption-key-32-chars", false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
os.Setenv("MEV_BOT_ENCRYPTION_KEY", tc.encryptionKey)
|
||||
defer os.Unsetenv("MEV_BOT_ENCRYPTION_KEY")
|
||||
|
||||
keyManagerConfig := &security.KeyManagerConfig{
|
||||
KeystorePath: "test_keystore_security",
|
||||
EncryptionKey: tc.encryptionKey,
|
||||
KeyRotationDays: 30,
|
||||
MaxSigningRate: 100,
|
||||
SessionTimeout: time.Hour,
|
||||
AuditLogPath: "test_audit_security.log",
|
||||
BackupPath: "test_backups_security",
|
||||
}
|
||||
|
||||
log := logger.New("debug", "text", "")
|
||||
_, err := security.NewKeyManager(keyManagerConfig, log)
|
||||
|
||||
if tc.shouldFail {
|
||||
assert.Error(t, err, "Should fail with %s", tc.name)
|
||||
} else {
|
||||
assert.NoError(t, err, "Should succeed with %s", tc.name)
|
||||
}
|
||||
|
||||
// Clean up
|
||||
os.RemoveAll("test_keystore_security")
|
||||
os.Remove("test_audit_security.log")
|
||||
os.RemoveAll("test_backups_security")
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("TestInputValidationAttacks", func(t *testing.T) {
|
||||
// Test various input attack scenarios
|
||||
attackAmounts := []*big.Int{
|
||||
big.NewInt(-1), // Negative
|
||||
big.NewInt(0), // Zero
|
||||
new(big.Int).Exp(big.NewInt(10), big.NewInt(50), nil), // Massive overflow
|
||||
new(big.Int).Exp(big.NewInt(2), big.NewInt(256), nil), // 2^256 overflow
|
||||
}
|
||||
|
||||
for i, amount := range attackAmounts {
|
||||
err := validateAmount(amount)
|
||||
assert.Error(t, err, "Should reject attack amount %d: %s", i, amount.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Helper functions for real-world testing
|
||||
|
||||
func setupRealEnvironment(t *testing.T) {
|
||||
// Set required environment variables for testing
|
||||
if os.Getenv("ARBITRUM_RPC_ENDPOINT") == "" {
|
||||
os.Setenv("ARBITRUM_RPC_ENDPOINT", "https://arb1.arbitrum.io/rpc")
|
||||
}
|
||||
if os.Getenv("MEV_BOT_ENCRYPTION_KEY") == "" {
|
||||
os.Setenv("MEV_BOT_ENCRYPTION_KEY", "test-encryption-key-for-testing-32")
|
||||
}
|
||||
if os.Getenv("MEV_BOT_ALLOW_LOCALHOST") == "" {
|
||||
os.Setenv("MEV_BOT_ALLOW_LOCALHOST", "false")
|
||||
}
|
||||
}
|
||||
|
||||
// TestOpportunity represents test-specific arbitrage data (extends canonical ArbitrageOpportunity)
|
||||
type TestOpportunity struct {
|
||||
*types.ArbitrageOpportunity
|
||||
Pool common.Address
|
||||
}
|
||||
|
||||
func detectRealArbitrageOpportunities(client *ethclient.Client, pool common.Address, log *logger.Logger) ([]*types.ArbitrageOpportunity, error) {
|
||||
// Query real pool state and detect actual arbitrage opportunities
|
||||
poolData, err := queryUniswapV3Pool(client, pool)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Compare with Camelot prices
|
||||
camelotRouter := common.HexToAddress("0xc873fEcbd354f5A56E00E710B90EF4201db2448d")
|
||||
testAmount := big.NewInt(1000000000000000000) // 1 WETH
|
||||
|
||||
camelotPrice, err := queryCamelotPrice(client, camelotRouter, poolData.Token0, poolData.Token1, testAmount)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Calculate potential arbitrage profit
|
||||
uniswapPrice := poolData.Price
|
||||
priceDiff := new(big.Int).Sub(camelotPrice, uniswapPrice)
|
||||
|
||||
var opportunities []*types.ArbitrageOpportunity
|
||||
|
||||
if priceDiff.Sign() > 0 {
|
||||
// Potential arbitrage opportunity
|
||||
minProfitThreshold := big.NewInt(50000000000000000) // 0.05 ETH
|
||||
|
||||
if priceDiff.Cmp(minProfitThreshold) >= 0 {
|
||||
opportunity := &types.ArbitrageOpportunity{
|
||||
Path: []string{poolData.Token0.Hex(), poolData.Token1.Hex()},
|
||||
Pools: []string{pool.Hex()},
|
||||
AmountIn: testAmount,
|
||||
Profit: priceDiff,
|
||||
NetProfit: priceDiff,
|
||||
GasEstimate: big.NewInt(800000),
|
||||
ROI: calculateROI(priceDiff, testAmount),
|
||||
Protocol: "test-arbitrage",
|
||||
ExecutionTime: 10000, // 10 seconds
|
||||
Confidence: 0.8, // Test confidence
|
||||
PriceImpact: 0.005, // 0.5% estimated
|
||||
MaxSlippage: 0.01, // 1% max slippage
|
||||
TokenIn: poolData.Token0,
|
||||
TokenOut: poolData.Token1,
|
||||
Timestamp: time.Now().Unix(),
|
||||
Risk: 0.2, // Medium risk for test
|
||||
}
|
||||
opportunities = append(opportunities, opportunity)
|
||||
}
|
||||
}
|
||||
|
||||
return opportunities, nil
|
||||
}
|
||||
|
||||
type PoolData struct {
|
||||
Token0 common.Address
|
||||
Token1 common.Address
|
||||
Fee uint32
|
||||
Liquidity *big.Int
|
||||
Price *big.Int
|
||||
}
|
||||
|
||||
func queryUniswapV3Pool(client *ethclient.Client, poolAddress common.Address) (*PoolData, error) {
|
||||
// In a real implementation, this would query the actual Uniswap V3 pool contract
|
||||
// For testing, we'll return mock data based on known pool structure
|
||||
|
||||
// WETH/USDC pool data (mock but realistic)
|
||||
return &PoolData{
|
||||
Token0: common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1"), // WETH
|
||||
Token1: common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831"), // USDC
|
||||
Fee: 500, // 0.05%
|
||||
Liquidity: big.NewInt(1000000000000000000000), // 1000 ETH equivalent
|
||||
Price: big.NewInt(2000000000), // ~2000 USDC per ETH
|
||||
}, nil
|
||||
}
|
||||
|
||||
func queryCamelotPrice(client *ethclient.Client, router common.Address, tokenIn, tokenOut common.Address, amountIn *big.Int) (*big.Int, error) {
|
||||
// In a real implementation, this would query the actual Camelot router
|
||||
// For testing, we'll return a slightly different price to simulate arbitrage opportunity
|
||||
|
||||
// Simulate 0.1% price difference (arbitrage opportunity)
|
||||
basePrice := big.NewInt(2000000000) // 2000 USDC
|
||||
priceDiff := big.NewInt(2000000) // 0.1% difference = 2 USDC
|
||||
|
||||
return new(big.Int).Add(basePrice, priceDiff), nil
|
||||
}
|
||||
|
||||
func queryTokenSupply(client *ethclient.Client, tokenAddress common.Address) (*big.Int, error) {
|
||||
// In a real implementation, this would query the actual token contract
|
||||
// For testing, return a realistic WETH total supply
|
||||
return big.NewInt(1000000000000000000000000), nil // 1M WETH
|
||||
}
|
||||
|
||||
func calculateROI(profit, investment *big.Int) float64 {
|
||||
if investment.Sign() == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
profitFloat := new(big.Float).SetInt(profit)
|
||||
investmentFloat := new(big.Float).SetInt(investment)
|
||||
|
||||
roi := new(big.Float).Quo(profitFloat, investmentFloat)
|
||||
roiFloat, _ := roi.Float64()
|
||||
|
||||
return roiFloat * 100 // Convert to percentage
|
||||
}
|
||||
|
||||
func validateRPCEndpoint(endpoint string) error {
|
||||
// Copy of the validation logic from main code
|
||||
if endpoint == "" {
|
||||
return fmt.Errorf("RPC endpoint cannot be empty")
|
||||
}
|
||||
|
||||
u, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid RPC endpoint URL: %w", err)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "http", "https", "ws", "wss":
|
||||
// Valid schemes
|
||||
default:
|
||||
return fmt.Errorf("invalid RPC scheme: %s", u.Scheme)
|
||||
}
|
||||
|
||||
if strings.Contains(u.Hostname(), "localhost") || strings.Contains(u.Hostname(), "127.0.0.1") {
|
||||
if os.Getenv("MEV_BOT_ALLOW_LOCALHOST") != "true" {
|
||||
return fmt.Errorf("localhost RPC endpoints not allowed")
|
||||
}
|
||||
}
|
||||
|
||||
if u.Hostname() == "" {
|
||||
return fmt.Errorf("RPC endpoint must have a valid hostname")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateAmount(amount *big.Int) error {
|
||||
if amount == nil || amount.Sign() <= 0 {
|
||||
return fmt.Errorf("amount must be greater than zero")
|
||||
}
|
||||
|
||||
maxAmount := new(big.Int).Exp(big.NewInt(10), big.NewInt(28), nil)
|
||||
if amount.Cmp(maxAmount) > 0 {
|
||||
return fmt.Errorf("amount exceeds maximum allowed value")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatEther(wei *big.Int) string {
|
||||
if wei == nil {
|
||||
return "0.000000"
|
||||
}
|
||||
eth := new(big.Float).SetInt(wei)
|
||||
eth.Quo(eth, big.NewFloat(1e18))
|
||||
return fmt.Sprintf("%.6f", eth)
|
||||
}
|
||||
|
||||
func formatGwei(wei *big.Int) string {
|
||||
if wei == nil {
|
||||
return "0.0"
|
||||
}
|
||||
gwei := new(big.Float).SetInt(wei)
|
||||
gwei.Quo(gwei, big.NewFloat(1e9))
|
||||
return fmt.Sprintf("%.2f", gwei)
|
||||
}
|
||||
Reference in New Issue
Block a user