package integration import ( "context" "fmt" "log" "os/exec" "runtime" "testing" "time" "github.com/ethereum/go-ethereum/ethclient" ) // setupForkedArbitrum sets up a forked Arbitrum test environment using anvil func setupForkedArbitrum(t testing.TB) (*ethclient.Client, func()) { // Check if anvil is available if _, err := exec.LookPath("anvil"); err != nil { t.Skip("anvil not found in PATH - install Foundry to run fork tests") } // Start anvil with Arbitrum fork arbitrumRPC := "https://arb1.arbitrum.io/rpc" port := "8545" cmd := exec.Command("anvil", "--fork-url", arbitrumRPC, "--port", port, "--gas-limit", "30000000", "--gas-price", "10000000000", // 10 gwei "--block-time", "1", // 1 second blocks "--accounts", "10", // 10 test accounts "--balance", "1000", // 1000 ETH per account ) // Start anvil in background if err := cmd.Start(); err != nil { t.Fatalf("Failed to start anvil: %v", err) } // Wait for anvil to be ready time.Sleep(3 * time.Second) // Connect to the forked network client, err := ethclient.Dial(fmt.Sprintf("http://localhost:%s", port)) if err != nil { cmd.Process.Kill() t.Fatalf("Failed to connect to forked Arbitrum: %v", err) } // Verify connection by getting chain ID ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() chainID, err := client.ChainID(ctx) if err != nil { cmd.Process.Kill() t.Fatalf("Failed to get chain ID: %v", err) } if chainID.Uint64() != 42161 { t.Logf("Warning: Expected Arbitrum chain ID 42161, got %d", chainID.Uint64()) } // Return cleanup function cleanup := func() { client.Close() if cmd.Process != nil { cmd.Process.Kill() cmd.Wait() } } return client, cleanup } // getMemStats returns current memory statistics func getMemStats() runtime.MemStats { var m runtime.MemStats runtime.ReadMemStats(&m) return m } // logMemoryUsage logs current memory usage for debugging func logMemoryUsage(t testing.TB, label string) { var m runtime.MemStats runtime.ReadMemStats(&m) t.Logf("%s - Memory: Alloc=%d KB, TotalAlloc=%d KB, Sys=%d KB, NumGC=%d", label, m.Alloc/1024, m.TotalAlloc/1024, m.Sys/1024, m.NumGC, ) } // waitForAnvil waits for anvil to be ready and responsive func waitForAnvil(port string, timeout time.Duration) error { deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { client, err := ethclient.Dial(fmt.Sprintf("http://localhost:%s", port)) if err == nil { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) _, err := client.ChainID(ctx) cancel() client.Close() if err == nil { return nil } } time.Sleep(500 * time.Millisecond) } return fmt.Errorf("anvil not ready after %v", timeout) } // createTestLogger creates a test logger for debugging func createTestLogger(t testing.TB) *log.Logger { return log.New(&testWriter{t: t}, "[TEST] ", log.LstdFlags|log.Lshortfile) } // testWriter implements io.Writer for test logging type testWriter struct { t testing.TB } func (tw *testWriter) Write(p []byte) (n int, err error) { tw.t.Log(string(p)) return len(p), nil }