package marketmanager import ( "context" "fmt" "sync" "time" "github.com/ethereum/go-ethereum/ethclient" ) // MarketManager handles market data collection, storage, and retrieval type MarketManager struct { // In-memory storage markets Markets mutex sync.RWMutex // Ethereum client for on-chain verification client *ethclient.Client // Configuration verificationWindow time.Duration // Time window for on-chain verification maxMarkets int // Maximum number of markets to store } // MarketManagerConfig holds configuration for the MarketManager type MarketManagerConfig struct { EthereumClient *ethclient.Client VerificationWindow time.Duration MaxMarkets int } // NewMarketManager creates a new MarketManager instance func NewMarketManager(config *MarketManagerConfig) *MarketManager { if config.VerificationWindow == 0 { config.VerificationWindow = 500 * time.Millisecond // Default 500ms } if config.MaxMarkets == 0 { config.MaxMarkets = 10000 // Default 10,000 markets } return &MarketManager{ markets: make(Markets), client: config.EthereumClient, verificationWindow: config.VerificationWindow, maxMarkets: config.MaxMarkets, } } // AddMarket adds a new market to the manager func (mm *MarketManager) AddMarket(market *Market) error { mm.mutex.Lock() defer mm.mutex.Unlock() // Check if we need to evict old markets if len(mm.markets) >= mm.maxMarkets { mm.evictOldestMarkets() } // Initialize the rawTicker map if it doesn't exist if mm.markets[market.RawTicker] == nil { mm.markets[market.RawTicker] = make(map[string]*Market) } // Add the market mm.markets[market.RawTicker][market.Key] = market return nil } // GetMarket retrieves a market by rawTicker and marketKey func (mm *MarketManager) GetMarket(rawTicker, marketKey string) (*Market, error) { mm.mutex.RLock() defer mm.mutex.RUnlock() if marketsForTicker, exists := mm.markets[rawTicker]; exists { if market, exists := marketsForTicker[marketKey]; exists { return market, nil } } return nil, fmt.Errorf("market not found for rawTicker: %s, marketKey: %s", rawTicker, marketKey) } // GetMarketsByRawTicker retrieves all markets for a given rawTicker func (mm *MarketManager) GetMarketsByRawTicker(rawTicker string) (map[string]*Market, error) { mm.mutex.RLock() defer mm.mutex.RUnlock() if markets, exists := mm.markets[rawTicker]; exists { // Return a copy to avoid external modification result := make(map[string]*Market) for key, market := range markets { result[key] = market.Clone() } return result, nil } return nil, fmt.Errorf("no markets found for rawTicker: %s", rawTicker) } // GetAllMarkets retrieves all markets func (mm *MarketManager) GetAllMarkets() Markets { mm.mutex.RLock() defer mm.mutex.RUnlock() // Return a deep copy to avoid external modification result := make(Markets) for rawTicker, markets := range mm.markets { result[rawTicker] = make(map[string]*Market) for key, market := range markets { result[rawTicker][key] = market.Clone() } } return result } // UpdateMarket updates an existing market func (mm *MarketManager) UpdateMarket(market *Market) error { mm.mutex.Lock() defer mm.mutex.Unlock() if mm.markets[market.RawTicker] == nil { return fmt.Errorf("no markets found for rawTicker: %s", market.RawTicker) } if _, exists := mm.markets[market.RawTicker][market.Key]; !exists { return fmt.Errorf("market not found for rawTicker: %s, marketKey: %s", market.RawTicker, market.Key) } // Update the market mm.markets[market.RawTicker][market.Key] = market return nil } // RemoveMarket removes a market by rawTicker and marketKey func (mm *MarketManager) RemoveMarket(rawTicker, marketKey string) error { mm.mutex.Lock() defer mm.mutex.Unlock() if mm.markets[rawTicker] == nil { return fmt.Errorf("no markets found for rawTicker: %s", rawTicker) } if _, exists := mm.markets[rawTicker][marketKey]; !exists { return fmt.Errorf("market not found for rawTicker: %s, marketKey: %s", rawTicker, marketKey) } delete(mm.markets[rawTicker], marketKey) // Clean up empty rawTicker maps if len(mm.markets[rawTicker]) == 0 { delete(mm.markets, rawTicker) } return nil } // VerifyMarket verifies a market's transaction on-chain func (mm *MarketManager) VerifyMarket(ctx context.Context, market *Market) (bool, error) { if mm.client == nil { return false, fmt.Errorf("ethereum client not configured") } // Check if the transaction exists on-chain _, err := mm.client.TransactionReceipt(ctx, market.TxHash) if err != nil { return false, nil // Transaction not found, but not an error } // Transaction exists, market is confirmed return true, nil } // ScheduleVerification schedules verification of a market within the verification window func (mm *MarketManager) ScheduleVerification(market *Market) { go func() { // Wait for the verification window time.Sleep(mm.verificationWindow) // Create a context with timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Verify the market confirmed, err := mm.VerifyMarket(ctx, market) if err != nil { // Log error but don't fail fmt.Printf("Error verifying market %s: %v\n", market.Key, err) return } if confirmed { // Update market status to confirmed market.Status = StatusConfirmed // Update the market in storage mm.mutex.Lock() if mm.markets[market.RawTicker] != nil { if existingMarket, exists := mm.markets[market.RawTicker][market.Key]; exists { existingMarket.Status = StatusConfirmed existingMarket.Timestamp = time.Now().Unix() } } mm.mutex.Unlock() } else { // Mark as invalid if not confirmed mm.mutex.Lock() if mm.markets[market.RawTicker] != nil { if existingMarket, exists := mm.markets[market.RawTicker][market.Key]; exists { existingMarket.Status = StatusInvalid } } mm.mutex.Unlock() } }() } // GetMarketCount returns the total number of markets func (mm *MarketManager) GetMarketCount() int { mm.mutex.RLock() defer mm.mutex.RUnlock() count := 0 for _, markets := range mm.markets { count += len(markets) } return count } // GetRawTickerCount returns the number of unique rawTickers func (mm *MarketManager) GetRawTickerCount() int { mm.mutex.RLock() defer mm.mutex.RUnlock() return len(mm.markets) } // evictOldestMarkets removes the oldest markets when the limit is reached func (mm *MarketManager) evictOldestMarkets() { // This is a simple implementation that removes the first rawTicker // A more sophisticated implementation might remove based on last access time for rawTicker := range mm.markets { delete(mm.markets, rawTicker) break // Remove just one to make space } } // GetValidMarketsByRawTicker retrieves all valid markets for a given rawTicker func (mm *MarketManager) GetValidMarketsByRawTicker(rawTicker string) (map[string]*Market, error) { markets, err := mm.GetMarketsByRawTicker(rawTicker) if err != nil { return nil, err } validMarkets := make(map[string]*Market) for key, market := range markets { if market.IsValid() { validMarkets[key] = market } } return validMarkets, nil }