docs: add comprehensive V2 requirements documentation

- Created MODULARITY_REQUIREMENTS.md with component independence rules
- Created PROTOCOL_SUPPORT_REQUIREMENTS.md covering 13+ protocols
- Created TESTING_REQUIREMENTS.md enforcing 100% coverage
- Updated CLAUDE.md with strict feature/v2/* branch strategy

Requirements documented:
- Component modularity (standalone + integrated)
- 100% test coverage enforcement (non-negotiable)
- All DEX protocols (Uniswap V2/V3/V4, Curve, Balancer V2/V3, Kyber Classic/Elastic, Camelot V2/V3 with all Algebra variants)
- Proper decimal handling (critical for calculations)
- Pool caching with multi-index and O(1) mappings
- Market building with essential arbitrage detection values
- Price movement detection with decimal precision
- Transaction building (single and batch execution)
- Pool discovery and caching
- Comprehensive validation at all layers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Administrator
2025-11-10 14:26:56 +01:00
parent c54c569f30
commit f41adbe575
4 changed files with 2063 additions and 0 deletions

View File

@@ -0,0 +1,408 @@
# V2 Modularity Requirements
## Core Principle: Standalone Components
**Every component MUST be able to run independently OR as part of the integrated system.**
## Component Independence Rules
### 1. Zero Hard Dependencies
- Each component communicates through well-defined interfaces only
- NO direct imports between sibling components
- NO shared state except through explicit interfaces
- Each component has its own configuration
### 2. Standalone Executability
Every component must support:
```go
// Example: Each parser can be used standalone
parser := uniswap_v2.NewParser(logger, cache)
event, err := parser.ParseLog(log, tx)
// OR as part of factory
factory := parsers.NewFactory()
factory.Register(ProtocolUniswapV2, parser)
```
### 3. Interface-First Design
Define interfaces BEFORE implementation:
```go
// pkg/parsers/interface.go
type Parser interface {
ParseLog(log *types.Log, tx *types.Transaction) (*Event, error)
ParseReceipt(receipt *types.Receipt, tx *types.Transaction) ([]*Event, error)
SupportedProtocols() []Protocol
ValidateEvent(event *Event) error
}
// Each parser implements this independently
```
## Component Modularity Matrix
| Component | Standalone Use Case | Integrated Use Case | Interface |
|-----------|-------------------|---------------------|-----------|
| UniswapV2Parser | Parse individual V2 transactions | Part of ParserFactory | `Parser` |
| UniswapV3Parser | Parse individual V3 transactions | Part of ParserFactory | `Parser` |
| PoolCache | Standalone pool lookup service | Shared cache for all parsers | `PoolCache` |
| EventValidator | Validate any event independently | Part of validation pipeline | `Validator` |
| AddressIndex | Standalone address lookup | Part of multi-index cache | `Index` |
| TokenPairIndex | Standalone pair lookup | Part of multi-index cache | `Index` |
| BackgroundValidator | Standalone validation service | Part of monitoring pipeline | `BackgroundValidator` |
## Directory Structure for Modularity
```
pkg/
├── parsers/
│ ├── interface.go # Parser interface (shared)
│ ├── factory.go # Factory for integration
│ ├── uniswap_v2/ # Standalone package
│ │ ├── parser.go # Can be imported independently
│ │ ├── parser_test.go # Self-contained tests
│ │ └── README.md # Standalone usage docs
│ ├── uniswap_v3/ # Standalone package
│ │ ├── parser.go
│ │ ├── parser_test.go
│ │ └── README.md
│ └── sushiswap/ # Standalone package
│ ├── parser.go
│ ├── parser_test.go
│ └── README.md
├── cache/
│ ├── interface.go # Cache interfaces
│ ├── pool_cache.go # Main cache (uses indexes)
│ ├── indexes/ # Standalone index packages
│ │ ├── address/
│ │ │ ├── index.go # Standalone address index
│ │ │ └── index_test.go
│ │ ├── tokenpair/
│ │ │ ├── index.go # Standalone pair index
│ │ │ └── index_test.go
│ │ └── liquidity/
│ │ ├── index.go # Standalone liquidity index
│ │ └── index_test.go
├── validation/
│ ├── interface.go # Validator interface
│ ├── validator.go # Main validator
│ ├── rules/ # Standalone rule packages
│ │ ├── zero_address/
│ │ │ ├── rule.go # Standalone rule
│ │ │ └── rule_test.go
│ │ ├── zero_amount/
│ │ │ ├── rule.go
│ │ │ └── rule_test.go
│ │ └── pool_cache/
│ │ ├── rule.go
│ │ └── rule_test.go
│ └── background/
│ ├── validator.go # Standalone background validator
│ └── validator_test.go
```
## Testing Requirements for Modularity
### 1. Unit Tests (Component Isolation)
Each component has 100% independent unit tests:
```go
// pkg/parsers/uniswap_v2/parser_test.go
func TestParser_Standalone(t *testing.T) {
// NO dependencies on other parsers
// NO dependencies on factory
// Uses mocks for interfaces only
logger := NewMockLogger()
cache := NewMockCache()
parser := NewParser(logger, cache)
event, err := parser.ParseLog(mockLog, mockTx)
assert.NoError(t, err)
assert.NotNil(t, event)
}
```
### 2. Integration Tests (Component Composition)
Test components working together:
```go
// tests/integration/parser_factory_test.go
func TestParserFactory_Integration(t *testing.T) {
// Test all parsers working through factory
factory := parsers.NewFactory()
// Each parser registered independently
factory.Register(ProtocolUniswapV2, uniswap_v2.NewParser(logger, cache))
factory.Register(ProtocolUniswapV3, uniswap_v3.NewParser(logger, cache))
// Test factory routing
parser, err := factory.GetParser(ProtocolUniswapV2)
assert.NoError(t, err)
}
```
### 3. Standalone Executability Tests
Each component has example main:
```go
// examples/uniswap_v2_parser/main.go
func main() {
// Demonstrate standalone usage
logger := logger.New("info", "text", "")
cache := cache.NewPoolCache()
parser := uniswap_v2.NewParser(logger, cache)
// Parse single transaction
event, err := parser.ParseLog(log, tx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Parsed event: %+v\n", event)
}
```
## Dependency Injection Pattern
All components use constructor injection:
```go
// Bad: Hard dependency
type UniswapV2Parser struct {
cache *PoolCache // Hard dependency!
}
// Good: Interface dependency
type UniswapV2Parser struct {
cache PoolCache // Interface - can be mocked or replaced
}
func NewParser(logger Logger, cache PoolCache) *UniswapV2Parser {
return &UniswapV2Parser{
logger: logger,
cache: cache,
}
}
```
## Configuration Independence
Each component has its own config:
```go
// pkg/parsers/uniswap_v2/config.go
type Config struct {
EnableValidation bool
CacheTimeout time.Duration
MaxRetries int
}
// Standalone usage
config := uniswap_v2.Config{
EnableValidation: true,
CacheTimeout: 5 * time.Minute,
MaxRetries: 3,
}
parser := uniswap_v2.NewParserWithConfig(logger, cache, config)
// Integrated usage
factory.RegisterWithConfig(
ProtocolUniswapV2,
parser,
config,
)
```
## Interface Contracts
### Minimal Interface Surface
Each interface has 1-3 methods maximum:
```go
// Good: Focused interface
type Parser interface {
ParseLog(log *types.Log, tx *types.Transaction) (*Event, error)
}
// Bad: God interface
type Parser interface {
ParseLog(log *types.Log, tx *types.Transaction) (*Event, error)
ParseReceipt(receipt *types.Receipt, tx *types.Transaction) ([]*Event, error)
ValidateEvent(event *Event) error
GetStats() Stats
Configure(config Config) error
// ... too many responsibilities
}
```
### Interface Segregation
Split large interfaces:
```go
// Split responsibilities
type LogParser interface {
ParseLog(log *types.Log, tx *types.Transaction) (*Event, error)
}
type ReceiptParser interface {
ParseReceipt(receipt *types.Receipt, tx *types.Transaction) ([]*Event, error)
}
type EventValidator interface {
ValidateEvent(event *Event) error
}
// Compose as needed
type Parser interface {
LogParser
ReceiptParser
EventValidator
}
```
## Build Tags for Optional Components
Use build tags for optional features:
```go
// pkg/parsers/uniswap_v2/parser.go
// +build !minimal
// Full implementation with all features
// pkg/parsers/uniswap_v2/parser_minimal.go
// +build minimal
// Minimal implementation for embedded systems
```
Build options:
```bash
# Full build (all components)
go build ./...
# Minimal build (core only)
go build -tags minimal ./...
# Custom build (specific parsers only)
go build -tags "uniswap_v2 uniswap_v3" ./...
```
## Component Communication Patterns
### 1. Synchronous (Direct Call)
For tight coupling when needed:
```go
event, err := parser.ParseLog(log, tx)
```
### 2. Asynchronous (Channels)
For loose coupling:
```go
eventChan := make(chan *Event, 100)
go parser.ParseAsync(logs, eventChan)
```
### 3. Pub/Sub (Event Bus)
For many-to-many communication:
```go
bus := eventbus.New()
parser.Subscribe(bus, "parsed")
validator.Subscribe(bus, "parsed")
bus.Publish("parsed", event)
```
### 4. Interface Composition
For static composition:
```go
type CompositeParser struct {
v2 *uniswap_v2.Parser
v3 *uniswap_v3.Parser
}
func (c *CompositeParser) Parse(log *types.Log) (*Event, error) {
// Route to appropriate parser
}
```
## Success Criteria for Modularity
Each component MUST:
- [ ] Compile independently (`go build ./pkg/parsers/uniswap_v2`)
- [ ] Test independently (`go test ./pkg/parsers/uniswap_v2`)
- [ ] Run standalone example (`go run examples/uniswap_v2_parser/main.go`)
- [ ] Have zero sibling dependencies
- [ ] Communicate only through interfaces
- [ ] Include standalone usage documentation
- [ ] Pass integration tests when composed
- [ ] Support mock implementations for testing
## Anti-Patterns to Avoid
### ❌ Circular Dependencies
```go
// pkg/parsers/uniswap_v2/parser.go
import "pkg/parsers/uniswap_v3" // BAD!
// pkg/parsers/uniswap_v3/parser.go
import "pkg/parsers/uniswap_v2" // BAD!
```
### ❌ Shared Mutable State
```go
// BAD: Global shared state
var globalCache *PoolCache
func (p *Parser) Parse(log *types.Log) (*Event, error) {
pool := globalCache.Get(log.Address) // BAD!
}
```
### ❌ Hard-coded Dependencies
```go
// BAD: Creates own dependencies
func NewParser() *Parser {
return &Parser{
cache: NewPoolCache(), // BAD! Should be injected
}
}
```
### ❌ Leaky Abstractions
```go
// BAD: Exposes internal structure
type Parser interface {
GetInternalCache() *PoolCache // BAD! Leaks implementation
}
```
## Migration Strategy
When moving from V1 to V2:
1. **Extract Interface**: Define interface from V1 implementation
2. **Create Package**: Move to standalone package
3. **Inject Dependencies**: Replace hard dependencies with interfaces
4. **Add Tests**: Unit tests for standalone operation
5. **Create Example**: Standalone usage example
6. **Document**: README with standalone and integrated usage
7. **Verify**: Check all modularity criteria
## Component Checklist
Before marking any component as "complete":
- [ ] Compiles independently
- [ ] Tests independently (>90% coverage)
- [ ] Has standalone example
- [ ] Has README with usage
- [ ] Zero sibling dependencies
- [ ] All dependencies injected through interfaces
- [ ] Can be mocked for testing
- [ ] Integrated into factory/orchestrator
- [ ] Integration tests pass
- [ ] Performance benchmarks exist
- [ ] Documentation complete
---
**Principle**: If you can't run it standalone, it's not modular enough.
**Guideline**: If you can't mock it, it's too coupled.
**Rule**: If it has circular dependencies, redesign it.