fix: resolve all compilation issues across transport and lifecycle packages
- Fixed duplicate type declarations in transport package - Removed unused variables in lifecycle and dependency injection - Fixed big.Int arithmetic operations in uniswap contracts - Added missing methods to MetricsCollector (IncrementCounter, RecordLatency, etc.) - Fixed jitter calculation in TCP transport retry logic - Updated ComponentHealth field access to use transport type - Ensured all core packages build successfully All major compilation errors resolved: ✅ Transport package builds clean ✅ Lifecycle package builds clean ✅ Main MEV bot application builds clean ✅ Fixed method signature mismatches ✅ Resolved type conflicts and duplications 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
718
test/golden_file_test.go
Normal file
718
test/golden_file_test.go
Normal file
@@ -0,0 +1,718 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/fraktal/mev-beta/internal/logger"
|
||||
"github.com/fraktal/mev-beta/pkg/arbitrum"
|
||||
"github.com/fraktal/mev-beta/pkg/events"
|
||||
"github.com/fraktal/mev-beta/pkg/oracle"
|
||||
)
|
||||
|
||||
// GoldenFileTest represents a test case with expected output
|
||||
type GoldenFileTest struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Input GoldenFileInput `json:"input"`
|
||||
Expected GoldenFileExpectedOutput `json:"expected"`
|
||||
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type GoldenFileInput struct {
|
||||
TransactionHash string `json:"transaction_hash"`
|
||||
BlockNumber uint64 `json:"block_number"`
|
||||
TransactionData string `json:"transaction_data"`
|
||||
To string `json:"to"`
|
||||
From string `json:"from"`
|
||||
Value string `json:"value"`
|
||||
GasUsed uint64 `json:"gas_used"`
|
||||
GasPrice string `json:"gas_price"`
|
||||
Logs []LogData `json:"logs,omitempty"`
|
||||
Receipt *ReceiptData `json:"receipt,omitempty"`
|
||||
}
|
||||
|
||||
type LogData struct {
|
||||
Address string `json:"address"`
|
||||
Topics []string `json:"topics"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type ReceiptData struct {
|
||||
Status uint64 `json:"status"`
|
||||
CumulativeGasUsed uint64 `json:"cumulative_gas_used"`
|
||||
Logs []LogData `json:"logs"`
|
||||
}
|
||||
|
||||
type GoldenFileExpectedOutput struct {
|
||||
// Parser Output Validation
|
||||
ShouldParse bool `json:"should_parse"`
|
||||
ParsedSuccessfully bool `json:"parsed_successfully"`
|
||||
|
||||
// DEX Interaction Details
|
||||
Protocol string `json:"protocol"`
|
||||
FunctionName string `json:"function_name"`
|
||||
FunctionSignature string `json:"function_signature"`
|
||||
|
||||
// Token Information
|
||||
TokenIn TokenInfo `json:"token_in"`
|
||||
TokenOut TokenInfo `json:"token_out"`
|
||||
|
||||
// Amount Validation
|
||||
AmountIn AmountValidation `json:"amount_in"`
|
||||
AmountOut AmountValidation `json:"amount_out"`
|
||||
AmountMinimum AmountValidation `json:"amount_minimum,omitempty"`
|
||||
|
||||
// Pool Information
|
||||
PoolAddress string `json:"pool_address,omitempty"`
|
||||
PoolFee uint32 `json:"pool_fee,omitempty"`
|
||||
PoolType string `json:"pool_type,omitempty"`
|
||||
|
||||
// Uniswap V3 Specific
|
||||
SqrtPriceX96 string `json:"sqrt_price_x96,omitempty"`
|
||||
Liquidity string `json:"liquidity,omitempty"`
|
||||
Tick *int `json:"tick,omitempty"`
|
||||
|
||||
// Price Information
|
||||
PriceImpact *PriceImpactInfo `json:"price_impact,omitempty"`
|
||||
Slippage *SlippageInfo `json:"slippage,omitempty"`
|
||||
|
||||
// MEV Analysis
|
||||
MEVOpportunity *MEVOpportunityInfo `json:"mev_opportunity,omitempty"`
|
||||
|
||||
// Events Validation
|
||||
ExpectedEvents []ExpectedEventValidation `json:"expected_events"`
|
||||
|
||||
// Error Validation
|
||||
ExpectedErrors []string `json:"expected_errors,omitempty"`
|
||||
ErrorPatterns []string `json:"error_patterns,omitempty"`
|
||||
|
||||
// Performance Metrics
|
||||
MaxParsingTimeMs uint64 `json:"max_parsing_time_ms,omitempty"`
|
||||
MaxMemoryUsageBytes uint64 `json:"max_memory_usage_bytes,omitempty"`
|
||||
}
|
||||
|
||||
type TokenInfo struct {
|
||||
Address string `json:"address"`
|
||||
Symbol string `json:"symbol"`
|
||||
Decimals uint8 `json:"decimals"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
type AmountValidation struct {
|
||||
RawValue string `json:"raw_value"`
|
||||
HumanReadable string `json:"human_readable"`
|
||||
USD *string `json:"usd,omitempty"`
|
||||
Precision string `json:"precision"`
|
||||
ShouldBePositive bool `json:"should_be_positive"`
|
||||
ShouldBeNonZero bool `json:"should_be_non_zero"`
|
||||
MaxValue *string `json:"max_value,omitempty"`
|
||||
MinValue *string `json:"min_value,omitempty"`
|
||||
}
|
||||
|
||||
type PriceImpactInfo struct {
|
||||
Percentage float64 `json:"percentage"`
|
||||
PriceBeforeSwap string `json:"price_before_swap"`
|
||||
PriceAfterSwap string `json:"price_after_swap"`
|
||||
ImpactClassification string `json:"impact_classification"` // "low", "medium", "high", "extreme"
|
||||
}
|
||||
|
||||
type SlippageInfo struct {
|
||||
RequestedBps uint64 `json:"requested_bps"`
|
||||
ActualBps *uint64 `json:"actual_bps,omitempty"`
|
||||
SlippageProtection bool `json:"slippage_protection"`
|
||||
ToleranceExceeded bool `json:"tolerance_exceeded"`
|
||||
}
|
||||
|
||||
type MEVOpportunityInfo struct {
|
||||
Type string `json:"type"` // "arbitrage", "sandwich", "liquidation"
|
||||
EstimatedProfitUSD *float64 `json:"estimated_profit_usd,omitempty"`
|
||||
GasCostUSD *float64 `json:"gas_cost_usd,omitempty"`
|
||||
NetProfitUSD *float64 `json:"net_profit_usd,omitempty"`
|
||||
ProfitabilityScore *float64 `json:"profitability_score,omitempty"`
|
||||
RiskScore *float64 `json:"risk_score,omitempty"`
|
||||
ConfidenceScore *float64 `json:"confidence_score,omitempty"`
|
||||
}
|
||||
|
||||
type ExpectedEventValidation struct {
|
||||
EventType string `json:"event_type"`
|
||||
ContractAddress string `json:"contract_address"`
|
||||
TopicCount int `json:"topic_count"`
|
||||
DataLength int `json:"data_length"`
|
||||
ParsedFields map[string]interface{} `json:"parsed_fields"`
|
||||
}
|
||||
|
||||
// GoldenFileTestSuite manages golden file testing
|
||||
type GoldenFileTestSuite struct {
|
||||
testDir string
|
||||
goldenDir string
|
||||
l2Parser *arbitrum.ArbitrumL2Parser
|
||||
eventParser *events.EventParser
|
||||
logger *logger.Logger
|
||||
oracle *oracle.PriceOracle
|
||||
}
|
||||
|
||||
func NewGoldenFileTestSuite(t *testing.T) *GoldenFileTestSuite {
|
||||
// Get test directory
|
||||
_, currentFile, _, _ := runtime.Caller(0)
|
||||
testDir := filepath.Dir(currentFile)
|
||||
goldenDir := filepath.Join(testDir, "golden")
|
||||
|
||||
// Ensure golden directory exists
|
||||
err := os.MkdirAll(goldenDir, 0755)
|
||||
require.NoError(t, err, "Failed to create golden directory")
|
||||
|
||||
// Setup components
|
||||
testLogger := logger.NewLogger(logger.Config{
|
||||
Level: "debug",
|
||||
Format: "json",
|
||||
})
|
||||
|
||||
testOracle, err := oracle.NewPriceOracle(&oracle.Config{
|
||||
Providers: []oracle.Provider{
|
||||
{Name: "mock", Type: "mock"},
|
||||
},
|
||||
}, testLogger)
|
||||
require.NoError(t, err, "Failed to create price oracle")
|
||||
|
||||
l2Parser, err := arbitrum.NewArbitrumL2Parser("https://mock-rpc", testLogger, testOracle)
|
||||
require.NoError(t, err, "Failed to create L2 parser")
|
||||
|
||||
eventParser := events.NewEventParser()
|
||||
|
||||
return &GoldenFileTestSuite{
|
||||
testDir: testDir,
|
||||
goldenDir: goldenDir,
|
||||
l2Parser: l2Parser,
|
||||
eventParser: eventParser,
|
||||
logger: testLogger,
|
||||
oracle: testOracle,
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoldenFiles(t *testing.T) {
|
||||
suite := NewGoldenFileTestSuite(t)
|
||||
defer suite.l2Parser.Close()
|
||||
|
||||
// Generate golden files if they don't exist
|
||||
t.Run("GenerateGoldenFiles", func(t *testing.T) {
|
||||
suite.generateGoldenFiles(t)
|
||||
})
|
||||
|
||||
// Run validation tests against golden files
|
||||
t.Run("ValidateAgainstGoldenFiles", func(t *testing.T) {
|
||||
suite.validateAgainstGoldenFiles(t)
|
||||
})
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) generateGoldenFiles(t *testing.T) {
|
||||
if !suite.shouldRegenerateGoldenFiles() {
|
||||
t.Skip("Golden files exist and regeneration not forced")
|
||||
return
|
||||
}
|
||||
|
||||
// Create test cases for different scenarios
|
||||
testCases := []GoldenFileTest{
|
||||
suite.createUniswapV3SwapTest(),
|
||||
suite.createSushiSwapV2Test(),
|
||||
suite.createMulticallTest(),
|
||||
suite.createFailedTransactionTest(),
|
||||
suite.createComplexArbitrageTest(),
|
||||
suite.createLiquidationTest(),
|
||||
suite.createStableSwapTest(),
|
||||
suite.createHighValueSwapTest(),
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
goldenFile := filepath.Join(suite.goldenDir, testCase.Name+".json")
|
||||
|
||||
// Execute parsing
|
||||
actualOutput := suite.executeParsingTest(testCase.Input)
|
||||
|
||||
// Update test case with actual output
|
||||
testCase.Expected = actualOutput
|
||||
|
||||
// Write golden file
|
||||
data, err := json.MarshalIndent(testCase, "", " ")
|
||||
require.NoError(t, err, "Failed to marshal test case")
|
||||
|
||||
err = ioutil.WriteFile(goldenFile, data, 0644)
|
||||
require.NoError(t, err, "Failed to write golden file")
|
||||
|
||||
suite.logger.Info(fmt.Sprintf("Generated golden file: %s", goldenFile))
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) validateAgainstGoldenFiles(t *testing.T) {
|
||||
goldenFiles, err := filepath.Glob(filepath.Join(suite.goldenDir, "*.json"))
|
||||
require.NoError(t, err, "Failed to find golden files")
|
||||
|
||||
for _, goldenFile := range goldenFiles {
|
||||
testName := filepath.Base(goldenFile)
|
||||
testName = testName[:len(testName)-5] // Remove .json extension
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
// Load golden file
|
||||
data, err := ioutil.ReadFile(goldenFile)
|
||||
require.NoError(t, err, "Failed to read golden file")
|
||||
|
||||
var testCase GoldenFileTest
|
||||
err = json.Unmarshal(data, &testCase)
|
||||
require.NoError(t, err, "Failed to unmarshal golden file")
|
||||
|
||||
// Execute parsing
|
||||
actualOutput := suite.executeParsingTest(testCase.Input)
|
||||
|
||||
// Compare with expected output
|
||||
suite.compareOutputs(t, testCase.Expected, actualOutput, testName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) executeParsingTest(input GoldenFileInput) GoldenFileExpectedOutput {
|
||||
// Create transaction from input
|
||||
rawTx := arbitrum.RawL2Transaction{
|
||||
Hash: input.TransactionHash,
|
||||
From: input.From,
|
||||
To: input.To,
|
||||
Value: input.Value,
|
||||
Input: input.TransactionData,
|
||||
}
|
||||
|
||||
// Execute parsing
|
||||
parsedTx, err := suite.l2Parser.ParseDEXTransaction(rawTx)
|
||||
|
||||
// Build output structure
|
||||
output := GoldenFileExpectedOutput{
|
||||
ShouldParse: err == nil,
|
||||
ParsedSuccessfully: err == nil && parsedTx != nil,
|
||||
ExpectedEvents: []ExpectedEventValidation{},
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
output.ExpectedErrors = []string{err.Error()}
|
||||
return output
|
||||
}
|
||||
|
||||
if parsedTx != nil {
|
||||
output.Protocol = parsedTx.Protocol
|
||||
output.FunctionName = parsedTx.FunctionName
|
||||
output.FunctionSignature = parsedTx.FunctionSig
|
||||
|
||||
// Extract token information
|
||||
if parsedTx.SwapDetails != nil {
|
||||
output.TokenIn = TokenInfo{
|
||||
Address: parsedTx.SwapDetails.TokenIn,
|
||||
Symbol: suite.getTokenSymbol(parsedTx.SwapDetails.TokenIn),
|
||||
}
|
||||
output.TokenOut = TokenInfo{
|
||||
Address: parsedTx.SwapDetails.TokenOut,
|
||||
Symbol: suite.getTokenSymbol(parsedTx.SwapDetails.TokenOut),
|
||||
}
|
||||
|
||||
// Extract amounts
|
||||
if parsedTx.SwapDetails.AmountIn != nil {
|
||||
output.AmountIn = AmountValidation{
|
||||
RawValue: parsedTx.SwapDetails.AmountIn.String(),
|
||||
HumanReadable: suite.formatAmount(parsedTx.SwapDetails.AmountIn),
|
||||
ShouldBePositive: true,
|
||||
ShouldBeNonZero: true,
|
||||
}
|
||||
}
|
||||
|
||||
if parsedTx.SwapDetails.AmountOut != nil {
|
||||
output.AmountOut = AmountValidation{
|
||||
RawValue: parsedTx.SwapDetails.AmountOut.String(),
|
||||
HumanReadable: suite.formatAmount(parsedTx.SwapDetails.AmountOut),
|
||||
ShouldBePositive: true,
|
||||
ShouldBeNonZero: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Extract pool information
|
||||
if parsedTx.SwapDetails.Fee > 0 {
|
||||
output.PoolFee = parsedTx.SwapDetails.Fee
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) compareOutputs(t *testing.T, expected, actual GoldenFileExpectedOutput, testName string) {
|
||||
// Compare basic parsing results
|
||||
assert.Equal(t, expected.ShouldParse, actual.ShouldParse,
|
||||
"Parsing success should match expected")
|
||||
assert.Equal(t, expected.ParsedSuccessfully, actual.ParsedSuccessfully,
|
||||
"Parse success status should match expected")
|
||||
|
||||
// Compare protocol information
|
||||
if expected.Protocol != "" {
|
||||
assert.Equal(t, expected.Protocol, actual.Protocol,
|
||||
"Protocol should match expected")
|
||||
}
|
||||
|
||||
if expected.FunctionName != "" {
|
||||
assert.Equal(t, expected.FunctionName, actual.FunctionName,
|
||||
"Function name should match expected")
|
||||
}
|
||||
|
||||
if expected.FunctionSignature != "" {
|
||||
assert.Equal(t, expected.FunctionSignature, actual.FunctionSignature,
|
||||
"Function signature should match expected")
|
||||
}
|
||||
|
||||
// Compare token information
|
||||
suite.compareTokenInfo(t, expected.TokenIn, actual.TokenIn, "TokenIn")
|
||||
suite.compareTokenInfo(t, expected.TokenOut, actual.TokenOut, "TokenOut")
|
||||
|
||||
// Compare amounts
|
||||
suite.compareAmountValidation(t, expected.AmountIn, actual.AmountIn, "AmountIn")
|
||||
suite.compareAmountValidation(t, expected.AmountOut, actual.AmountOut, "AmountOut")
|
||||
|
||||
// Compare errors
|
||||
if len(expected.ExpectedErrors) > 0 {
|
||||
assert.Equal(t, len(expected.ExpectedErrors), len(actual.ExpectedErrors),
|
||||
"Error count should match expected")
|
||||
|
||||
for i, expectedError := range expected.ExpectedErrors {
|
||||
if i < len(actual.ExpectedErrors) {
|
||||
assert.Contains(t, actual.ExpectedErrors[i], expectedError,
|
||||
"Actual error should contain expected error message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compare events if present
|
||||
if len(expected.ExpectedEvents) > 0 {
|
||||
assert.Equal(t, len(expected.ExpectedEvents), len(actual.ExpectedEvents),
|
||||
"Event count should match expected")
|
||||
|
||||
for i, expectedEvent := range expected.ExpectedEvents {
|
||||
if i < len(actual.ExpectedEvents) {
|
||||
suite.compareEventValidation(t, expectedEvent, actual.ExpectedEvents[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) compareTokenInfo(t *testing.T, expected, actual TokenInfo, fieldName string) {
|
||||
if expected.Address != "" {
|
||||
assert.Equal(t, expected.Address, actual.Address,
|
||||
fmt.Sprintf("%s address should match expected", fieldName))
|
||||
}
|
||||
|
||||
if expected.Symbol != "" {
|
||||
assert.Equal(t, expected.Symbol, actual.Symbol,
|
||||
fmt.Sprintf("%s symbol should match expected", fieldName))
|
||||
}
|
||||
|
||||
if expected.Decimals > 0 {
|
||||
assert.Equal(t, expected.Decimals, actual.Decimals,
|
||||
fmt.Sprintf("%s decimals should match expected", fieldName))
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) compareAmountValidation(t *testing.T, expected, actual AmountValidation, fieldName string) {
|
||||
if expected.RawValue != "" {
|
||||
assert.Equal(t, expected.RawValue, actual.RawValue,
|
||||
fmt.Sprintf("%s raw value should match expected", fieldName))
|
||||
}
|
||||
|
||||
if expected.HumanReadable != "" {
|
||||
assert.Equal(t, expected.HumanReadable, actual.HumanReadable,
|
||||
fmt.Sprintf("%s human readable should match expected", fieldName))
|
||||
}
|
||||
|
||||
if expected.ShouldBePositive {
|
||||
amountBig, ok := new(big.Int).SetString(actual.RawValue, 10)
|
||||
if ok {
|
||||
assert.True(t, amountBig.Cmp(big.NewInt(0)) > 0,
|
||||
fmt.Sprintf("%s should be positive", fieldName))
|
||||
}
|
||||
}
|
||||
|
||||
if expected.ShouldBeNonZero {
|
||||
amountBig, ok := new(big.Int).SetString(actual.RawValue, 10)
|
||||
if ok {
|
||||
assert.True(t, amountBig.Cmp(big.NewInt(0)) != 0,
|
||||
fmt.Sprintf("%s should be non-zero", fieldName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) compareEventValidation(t *testing.T, expected, actual ExpectedEventValidation) {
|
||||
assert.Equal(t, expected.EventType, actual.EventType,
|
||||
"Event type should match expected")
|
||||
assert.Equal(t, expected.ContractAddress, actual.ContractAddress,
|
||||
"Event contract address should match expected")
|
||||
assert.Equal(t, expected.TopicCount, actual.TopicCount,
|
||||
"Event topic count should match expected")
|
||||
}
|
||||
|
||||
// Test case generators
|
||||
|
||||
func (suite *GoldenFileTestSuite) createUniswapV3SwapTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "uniswap_v3_exact_input_single",
|
||||
Description: "Uniswap V3 exactInputSingle USDC -> WETH swap",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_uniswap_v3_swap",
|
||||
BlockNumber: 150234567,
|
||||
TransactionData: "0x414bf389" + suite.createExactInputSingleData(),
|
||||
To: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 150000,
|
||||
GasPrice: "100000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createSushiSwapV2Test() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "sushiswap_v2_exact_tokens",
|
||||
Description: "SushiSwap V2 swapExactTokensForTokens",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_sushiswap_v2_swap",
|
||||
BlockNumber: 150234568,
|
||||
TransactionData: "0x38ed1739" + suite.createSwapExactTokensData(),
|
||||
To: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 120000,
|
||||
GasPrice: "100000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createMulticallTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "multicall_batch_operations",
|
||||
Description: "Multicall with multiple DEX operations",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_multicall_batch",
|
||||
BlockNumber: 150234569,
|
||||
TransactionData: "0xac9650d8" + suite.createMulticallData(),
|
||||
To: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 350000,
|
||||
GasPrice: "120000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createFailedTransactionTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "failed_slippage_exceeded",
|
||||
Description: "Failed transaction due to slippage protection",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_failed_slippage",
|
||||
BlockNumber: 150234570,
|
||||
TransactionData: "0x414bf389" + suite.createExactInputSingleData(),
|
||||
To: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 45000,
|
||||
GasPrice: "100000000",
|
||||
Receipt: &ReceiptData{
|
||||
Status: 0, // Failed
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createComplexArbitrageTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "complex_arbitrage_mev",
|
||||
Description: "Complex multi-DEX arbitrage transaction",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_arbitrage_complex",
|
||||
BlockNumber: 150234571,
|
||||
TransactionData: "0x7c025200" + suite.create1InchAggregatorData(),
|
||||
To: "0x1111111254EEB25477B68fb85Ed929f73A960582",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 450000,
|
||||
GasPrice: "150000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createLiquidationTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "liquidation_aave_position",
|
||||
Description: "Aave liquidation with DEX swap",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_liquidation_aave",
|
||||
BlockNumber: 150234572,
|
||||
TransactionData: "0x38ed1739" + suite.createSwapExactTokensData(),
|
||||
To: "0x1b02dA8Cb0d097eB8D57A175b88c7D8b47997506",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 280000,
|
||||
GasPrice: "130000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createStableSwapTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "curve_stable_swap",
|
||||
Description: "Curve stable coin swap USDC -> USDT",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_curve_stable",
|
||||
BlockNumber: 150234573,
|
||||
TransactionData: "0x3df02124" + suite.createCurveExchangeData(),
|
||||
To: "0x7f90122BF0700F9E7e1F688fe926940E8839F353",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 95000,
|
||||
GasPrice: "100000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createHighValueSwapTest() GoldenFileTest {
|
||||
return GoldenFileTest{
|
||||
Name: "high_value_swap_1million",
|
||||
Description: "High-value swap exceeding $1M",
|
||||
Input: GoldenFileInput{
|
||||
TransactionHash: "0xtest_high_value_1m",
|
||||
BlockNumber: 150234574,
|
||||
TransactionData: "0x414bf389" + suite.createHighValueExactInputSingleData(),
|
||||
To: "0xE592427A0AEce92De3Edee1F18E0157C05861564",
|
||||
From: "0x1234567890123456789012345678901234567890",
|
||||
Value: "0",
|
||||
GasUsed: 180000,
|
||||
GasPrice: "120000000",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for creating mock transaction data
|
||||
|
||||
func (suite *GoldenFileTestSuite) createExactInputSingleData() string {
|
||||
// Mock ExactInputSingleParams data (256 bytes)
|
||||
tokenIn := "000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831" // USDC
|
||||
tokenOut := "00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1" // WETH
|
||||
fee := "00000000000000000000000000000000000000000000000000000000000001f4" // 500
|
||||
recipient := "0000000000000000000000001234567890123456789012345678901234567890" // Recipient
|
||||
deadline := "0000000000000000000000000000000000000000000000000000000060000000" // Deadline
|
||||
amountIn := "00000000000000000000000000000000000000000000000000000e8d4a51000" // 1000 USDC
|
||||
amountOutMin := "0000000000000000000000000000000000000000000000000538bca4a7e000" // Min WETH out
|
||||
sqrtLimit := "0000000000000000000000000000000000000000000000000000000000000000" // No limit
|
||||
|
||||
return tokenIn + tokenOut + fee + recipient + deadline + amountIn + amountOutMin + sqrtLimit
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createSwapExactTokensData() string {
|
||||
// Mock swapExactTokensForTokens data (160 bytes + path)
|
||||
amountIn := "0000000000000000000000000000000000000000000000000de0b6b3a7640000" // 1 ETH
|
||||
amountOutMin := "00000000000000000000000000000000000000000000000000000ba43b7400" // Min out
|
||||
pathOffset := "00000000000000000000000000000000000000000000000000000000000000a0" // Path offset
|
||||
recipient := "0000000000000000000000001234567890123456789012345678901234567890" // Recipient
|
||||
deadline := "0000000000000000000000000000000000000000000000000000000060000000" // Deadline
|
||||
pathLength := "0000000000000000000000000000000000000000000000000000000000000002" // 2 tokens
|
||||
token0 := "00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1" // WETH
|
||||
token1 := "000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831" // USDC
|
||||
|
||||
return amountIn + amountOutMin + pathOffset + recipient + deadline + pathLength + token0 + token1
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createMulticallData() string {
|
||||
// Mock multicall data with array of calls
|
||||
offset := "0000000000000000000000000000000000000000000000000000000000000020" // Data offset
|
||||
length := "0000000000000000000000000000000000000000000000000000000000000003" // 3 calls
|
||||
call1Offset := "0000000000000000000000000000000000000000000000000000000000000060" // Call 1 offset
|
||||
call2Offset := "0000000000000000000000000000000000000000000000000000000000000100" // Call 2 offset
|
||||
call3Offset := "0000000000000000000000000000000000000000000000000000000000000180" // Call 3 offset
|
||||
|
||||
// Simplified call data
|
||||
call1Data := "0000000000000000000000000000000000000000000000000000000000000040" + "414bf389" + strings.Repeat("0", 120)
|
||||
call2Data := "0000000000000000000000000000000000000000000000000000000000000040" + "38ed1739" + strings.Repeat("0", 120)
|
||||
call3Data := "0000000000000000000000000000000000000000000000000000000000000040" + "db3e2198" + strings.Repeat("0", 120)
|
||||
|
||||
return offset + length + call1Offset + call2Offset + call3Offset + call1Data + call2Data + call3Data
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) create1InchAggregatorData() string {
|
||||
// Mock 1inch aggregator swap data
|
||||
return strings.Repeat("0", 512) // Simplified aggregator data
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createCurveExchangeData() string {
|
||||
// Mock Curve exchange parameters
|
||||
i := "0000000000000000000000000000000000000000000000000000000000000001" // From token index (USDC)
|
||||
j := "0000000000000000000000000000000000000000000000000000000000000002" // To token index (USDT)
|
||||
dx := "00000000000000000000000000000000000000000000000000000e8d4a51000" // 1000 USDC
|
||||
minDy := "00000000000000000000000000000000000000000000000000000e78b7ee00" // Min 999 USDT
|
||||
|
||||
return i + j + dx + minDy
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) createHighValueExactInputSingleData() string {
|
||||
// High-value version of exactInputSingle (1M USDC)
|
||||
tokenIn := "000000000000000000000000af88d065e77c8cc2239327c5edb3a432268e5831" // USDC
|
||||
tokenOut := "00000000000000000000000082af49447d8a07e3bd95bd0d56f35241523fbab1" // WETH
|
||||
fee := "00000000000000000000000000000000000000000000000000000000000001f4" // 500
|
||||
recipient := "0000000000000000000000001234567890123456789012345678901234567890" // Recipient
|
||||
deadline := "0000000000000000000000000000000000000000000000000000000060000000" // Deadline
|
||||
amountIn := "000000000000000000000000000000000000000000000000d3c21bcecceda1000000" // 1M USDC (1,000,000 * 10^6)
|
||||
amountOutMin := "0000000000000000000000000000000000000000000000001158e460913d0000" // Min WETH out
|
||||
sqrtLimit := "0000000000000000000000000000000000000000000000000000000000000000" // No limit
|
||||
|
||||
return tokenIn + tokenOut + fee + recipient + deadline + amountIn + amountOutMin + sqrtLimit
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
func (suite *GoldenFileTestSuite) shouldRegenerateGoldenFiles() bool {
|
||||
// Check if REGENERATE_GOLDEN environment variable is set
|
||||
return os.Getenv("REGENERATE_GOLDEN") == "true"
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) getTokenSymbol(address string) string {
|
||||
// Mock token symbol resolution
|
||||
tokenMap := map[string]string{
|
||||
"0x82af49447d8a07e3bd95bd0d56f35241523fbab1": "WETH",
|
||||
"0xaf88d065e77c8cc2239327c5edb3a432268e5831": "USDC",
|
||||
"0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9": "USDT",
|
||||
"0x912ce59144191c1204e64559fe8253a0e49e6548": "ARB",
|
||||
}
|
||||
|
||||
if symbol, exists := tokenMap[strings.ToLower(address)]; exists {
|
||||
return symbol
|
||||
}
|
||||
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func (suite *GoldenFileTestSuite) formatAmount(amount *big.Int) string {
|
||||
if amount == nil {
|
||||
return "0"
|
||||
}
|
||||
|
||||
// Format with 18 decimals (simplified)
|
||||
divisor := new(big.Int).Exp(big.NewInt(10), big.NewInt(18), nil)
|
||||
quotient := new(big.Int).Div(amount, divisor)
|
||||
remainder := new(big.Int).Mod(amount, divisor)
|
||||
|
||||
if remainder.Cmp(big.NewInt(0)) == 0 {
|
||||
return quotient.String()
|
||||
}
|
||||
|
||||
// Simple decimal formatting
|
||||
return fmt.Sprintf("%s.%018s", quotient.String(), remainder.String())
|
||||
}
|
||||
Reference in New Issue
Block a user