Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
289 lines
9.2 KiB
Go
289 lines
9.2 KiB
Go
package marketmanager
|
|
|
|
import (
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
)
|
|
|
|
func TestMarketManagerCreation(t *testing.T) {
|
|
config := &MarketManagerConfig{
|
|
VerificationWindow: 500 * time.Millisecond,
|
|
MaxMarkets: 1000,
|
|
}
|
|
|
|
manager := NewMarketManager(config)
|
|
|
|
if manager == nil {
|
|
t.Error("Expected MarketManager to be created")
|
|
}
|
|
|
|
if manager.verificationWindow != 500*time.Millisecond {
|
|
t.Errorf("Expected verificationWindow 500ms, got %v", manager.verificationWindow)
|
|
}
|
|
|
|
if manager.maxMarkets != 1000 {
|
|
t.Errorf("Expected maxMarkets 1000, got %d", manager.maxMarkets)
|
|
}
|
|
}
|
|
|
|
func TestMarketManagerAddAndGetMarket(t *testing.T) {
|
|
manager := NewMarketManager(&MarketManagerConfig{})
|
|
|
|
market := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WETH",
|
|
RawTicker: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
Key: "test_key",
|
|
Price: big.NewFloat(2000.5),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
// Add market
|
|
err := manager.AddMarket(market)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when adding market, got %v", err)
|
|
}
|
|
|
|
// Get market
|
|
retrievedMarket, err := manager.GetMarket(market.RawTicker, market.Key)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when getting market, got %v", err)
|
|
}
|
|
|
|
if retrievedMarket.Ticker != market.Ticker {
|
|
t.Errorf("Expected ticker %s, got %s", market.Ticker, retrievedMarket.Ticker)
|
|
}
|
|
|
|
// Try to get non-existent market
|
|
_, err = manager.GetMarket("non_existent", "non_existent")
|
|
if err == nil {
|
|
t.Error("Expected error when getting non-existent market")
|
|
}
|
|
}
|
|
|
|
func TestMarketManagerGetMarketsByRawTicker(t *testing.T) {
|
|
manager := NewMarketManager(&MarketManagerConfig{})
|
|
|
|
// Add multiple markets with the same rawTicker
|
|
rawTicker := "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
|
|
market1 := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WETH_3000",
|
|
RawTicker: rawTicker,
|
|
Key: "test_key_1",
|
|
Price: big.NewFloat(2000.5),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
market2 := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x7BeA39867e4169DBe237d55C8242a8f2fDcD53F0"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 500,
|
|
Ticker: "USDC_WETH_500",
|
|
RawTicker: rawTicker,
|
|
Key: "test_key_2",
|
|
Price: big.NewFloat(2001.0),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
// Add markets
|
|
manager.AddMarket(market1)
|
|
manager.AddMarket(market2)
|
|
|
|
// Get markets by rawTicker
|
|
markets, err := manager.GetMarketsByRawTicker(rawTicker)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when getting markets by rawTicker, got %v", err)
|
|
}
|
|
|
|
if len(markets) != 2 {
|
|
t.Errorf("Expected 2 markets, got %d", len(markets))
|
|
}
|
|
|
|
if markets[market1.Key].Ticker != market1.Ticker {
|
|
t.Errorf("Expected ticker %s, got %s", market1.Ticker, markets[market1.Key].Ticker)
|
|
}
|
|
|
|
if markets[market2.Key].Ticker != market2.Ticker {
|
|
t.Errorf("Expected ticker %s, got %s", market2.Ticker, markets[market2.Key].Ticker)
|
|
}
|
|
|
|
// Try to get markets for non-existent rawTicker
|
|
_, err = manager.GetMarketsByRawTicker("non_existent")
|
|
if err == nil {
|
|
t.Error("Expected error when getting markets for non-existent rawTicker")
|
|
}
|
|
}
|
|
|
|
func TestMarketManagerUpdateMarket(t *testing.T) {
|
|
manager := NewMarketManager(&MarketManagerConfig{})
|
|
|
|
market := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WETH",
|
|
RawTicker: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
Key: "test_key",
|
|
Price: big.NewFloat(2000.5),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
// Add market
|
|
manager.AddMarket(market)
|
|
|
|
// Update market price
|
|
newPrice := big.NewFloat(2100.0)
|
|
market.Price = newPrice
|
|
|
|
// Update market
|
|
err := manager.UpdateMarket(market)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when updating market, got %v", err)
|
|
}
|
|
|
|
// Get updated market
|
|
updatedMarket, err := manager.GetMarket(market.RawTicker, market.Key)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when getting updated market, got %v", err)
|
|
}
|
|
|
|
if updatedMarket.Price.Cmp(newPrice) != 0 {
|
|
t.Errorf("Expected price %v, got %v", newPrice, updatedMarket.Price)
|
|
}
|
|
|
|
// Try to update non-existent market
|
|
nonExistentMarket := &Market{
|
|
RawTicker: "non_existent",
|
|
Key: "non_existent",
|
|
}
|
|
|
|
err = manager.UpdateMarket(nonExistentMarket)
|
|
if err == nil {
|
|
t.Error("Expected error when updating non-existent market")
|
|
}
|
|
}
|
|
|
|
func TestMarketManagerRemoveMarket(t *testing.T) {
|
|
manager := NewMarketManager(&MarketManagerConfig{})
|
|
|
|
market := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WETH",
|
|
RawTicker: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
Key: "test_key",
|
|
Price: big.NewFloat(2000.5),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
// Add market
|
|
manager.AddMarket(market)
|
|
|
|
// Remove market
|
|
err := manager.RemoveMarket(market.RawTicker, market.Key)
|
|
if err != nil {
|
|
t.Errorf("Expected no error when removing market, got %v", err)
|
|
}
|
|
|
|
// Try to get removed market
|
|
_, err = manager.GetMarket(market.RawTicker, market.Key)
|
|
if err == nil {
|
|
t.Error("Expected error when getting removed market")
|
|
}
|
|
|
|
// Try to remove non-existent market
|
|
err = manager.RemoveMarket("non_existent", "non_existent")
|
|
if err == nil {
|
|
t.Error("Expected error when removing non-existent market")
|
|
}
|
|
}
|
|
|
|
func TestMarketManagerGetCounts(t *testing.T) {
|
|
manager := NewMarketManager(&MarketManagerConfig{})
|
|
|
|
// Initially should be zero
|
|
if manager.GetMarketCount() != 0 {
|
|
t.Errorf("Expected market count 0, got %d", manager.GetMarketCount())
|
|
}
|
|
|
|
if manager.GetRawTickerCount() != 0 {
|
|
t.Errorf("Expected raw ticker count 0, got %d", manager.GetRawTickerCount())
|
|
}
|
|
|
|
// Add markets
|
|
rawTicker1 := "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
|
rawTicker2 := "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48_0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" // USDC_WBTC
|
|
|
|
market1 := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WETH_3000",
|
|
RawTicker: rawTicker1,
|
|
Key: "test_key_1",
|
|
Price: big.NewFloat(2000.5),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
market2 := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0x7BeA39867e4169DBe237d55C8242a8f2fDcD53F0"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
|
|
Fee: 500,
|
|
Ticker: "USDC_WETH_500",
|
|
RawTicker: rawTicker1,
|
|
Key: "test_key_2",
|
|
Price: big.NewFloat(2001.0),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
market3 := &Market{
|
|
Factory: common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984"),
|
|
PoolAddress: common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"),
|
|
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
|
|
Token1: common.HexToAddress("0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"),
|
|
Fee: 3000,
|
|
Ticker: "USDC_WBTC",
|
|
RawTicker: rawTicker2,
|
|
Key: "test_key_3",
|
|
Price: big.NewFloat(50000.0),
|
|
Protocol: "UniswapV3",
|
|
}
|
|
|
|
manager.AddMarket(market1)
|
|
manager.AddMarket(market2)
|
|
manager.AddMarket(market3)
|
|
|
|
// Check counts
|
|
if manager.GetMarketCount() != 3 {
|
|
t.Errorf("Expected market count 3, got %d", manager.GetMarketCount())
|
|
}
|
|
|
|
if manager.GetRawTickerCount() != 2 {
|
|
t.Errorf("Expected raw ticker count 2, got %d", manager.GetRawTickerCount())
|
|
}
|
|
}
|