Files
mev-beta/orig/pkg/calldata/swaps_payload_test.go
Administrator 803de231ba 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>
2025-11-10 10:14:26 +01:00

176 lines
5.1 KiB
Go

package calldata
import (
"encoding/hex"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
)
type payloadFixture struct {
Protocol string `json:"protocol"`
Function string `json:"function"`
FunctionSig string `json:"function_sig"`
InputData string `json:"input_data"`
}
func loadPayload(t *testing.T, name string) payloadFixture {
t.Helper()
fullPath := filepath.Join("testdata", "payloads", name)
raw, err := os.ReadFile(fullPath)
if err != nil {
t.Fatalf("failed to read payload fixture %s: %v", name, err)
}
var pf payloadFixture
if err := json.Unmarshal(raw, &pf); err != nil {
t.Fatalf("failed to unmarshal payload %s: %v", name, err)
}
return pf
}
func decodeDirect(raw []byte, ctx *MulticallContext) []*SwapCall {
validator := getAddressValidator()
return decodeSwapCallRecursive(raw, ctx, validator, 0)
}
func TestCapturedPayloadDecoding(t *testing.T) {
cases := []struct {
name string
fixture string
expectSwaps int
expectSelector string
tokenIn string
tokenOut string
verifyProtocol string
isMulticall bool
}{
{
name: "uniswap_v3_exact_output_single",
fixture: "uniswapv3_exact_output_single.json",
expectSwaps: 1,
expectSelector: "db3e2198",
tokenIn: "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8",
tokenOut: "0x912ce59144191c1204e64559fe8253a0e49e6548",
verifyProtocol: "UniswapV3",
},
{
name: "uniswap_v3_exact_input_single",
fixture: "uniswapv3_exact_input_single.json",
expectSwaps: 1,
expectSelector: "414bf389",
tokenIn: "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
tokenOut: "0x440017a1b021006d556d7fc06a54c32e42eb745b",
verifyProtocol: "UniswapV3",
},
{
name: "uniswap_v2_swap_exact_tokens",
fixture: "uniswapv2_exact_tokens.json",
expectSwaps: 1,
expectSelector: "38ed1739",
tokenIn: "0x03f6921f6e948016631ce796331294d5a863a9ee",
tokenOut: "0xdcc9691793633176acf5cfdc1a658cb3b982e2fb",
verifyProtocol: "UniswapV2",
},
{
name: "multicall_without_recognised_swaps",
fixture: "multicall_uniswap.json",
expectSwaps: 0,
isMulticall: true,
},
{
name: "uniswap_v3_decrease_liquidity",
fixture: "uniswapv3_decrease_liquidity.json",
expectSwaps: 0,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
payload := loadPayload(t, tc.fixture)
raw, err := hex.DecodeString(strings.TrimPrefix(payload.InputData, "0x"))
if err != nil {
t.Fatalf("failed to decode input data: %v", err)
}
if len(raw) < 4 {
t.Fatalf("payload too short: %d bytes", len(raw))
}
ctx := &MulticallContext{Protocol: payload.Protocol}
var swaps []*SwapCall
if tc.isMulticall {
swaps, err = DecodeSwapCallsFromMulticall(raw[4:], ctx)
if err != nil {
t.Fatalf("multicall decode failed: %v", err)
}
} else {
swaps = decodeDirect(raw, ctx)
}
if len(swaps) != tc.expectSwaps {
selector := strings.ToLower(hex.EncodeToString(raw[:4]))
if !tc.isMulticall {
// attempt direct decode based on selector for diagnostics
var diag *SwapCall
switch selector {
case "414bf389":
diag = decodeExactInputSingle(raw[4:], ctx)
case "db3e2198":
diag = decodeExactInput(raw[4:], ctx)
case "38ed1739":
diag = decodeUniswapV2Swap(selector, raw[4:], ctx)
}
if diag != nil {
t.Fatalf("expected %d swaps, got %d (selector=%s, diag=%s)", tc.expectSwaps, len(swaps), selector, diag.String())
}
}
t.Fatalf("expected %d swaps, got %d (selector=%s)", tc.expectSwaps, len(swaps), selector)
}
if tc.expectSwaps == 0 {
return
}
swap := swaps[0]
if !selectorsMatch(swap.Selector, tc.expectSelector) {
t.Fatalf("expected selector %s, got %s", tc.expectSelector, swap.Selector)
}
if !strings.EqualFold(swap.TokenIn.Hex(), tc.tokenIn) {
t.Fatalf("expected tokenIn %s, got %s", tc.tokenIn, swap.TokenIn.Hex())
}
if !strings.EqualFold(swap.TokenOut.Hex(), tc.tokenOut) {
t.Fatalf("expected tokenOut %s, got %s", tc.tokenOut, swap.TokenOut.Hex())
}
if tc.verifyProtocol != "" && !strings.EqualFold(swap.Protocol, tc.verifyProtocol) {
t.Fatalf("expected protocol %s, got %s", tc.verifyProtocol, swap.Protocol)
}
if selectorEquals(tc.expectSelector, "db3e2198") {
if swap.AmountOut == nil || swap.AmountOut.Sign() == 0 {
t.Fatalf("expected non-zero amountOut for exactOutputSingle")
}
}
if selectorEquals(tc.expectSelector, "414bf389") || selectorEquals(tc.expectSelector, "38ed1739") {
if swap.AmountIn == nil || swap.AmountIn.Sign() == 0 {
t.Fatalf("expected non-zero amountIn for selector %s", tc.expectSelector)
}
}
})
}
}
func normalizeSelectorHex(sel string) string {
s := strings.TrimSpace(strings.ToLower(sel))
return strings.TrimPrefix(s, "0x")
}
func selectorEquals(a, b string) bool {
return normalizeSelectorHex(a) == normalizeSelectorHex(b)
}
func selectorsMatch(actual, expected string) bool {
return selectorEquals(actual, expected)
}