feat: create v2-prep branch with comprehensive planning
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>
This commit is contained in:
267
orig/pkg/marketmanager/manager.go
Normal file
267
orig/pkg/marketmanager/manager.go
Normal file
@@ -0,0 +1,267 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user