Skip to content

plan: x402 V2 Migration #2

@rekon307

Description

@rekon307

x402 V2 Migration Plan (Deepened)

Created: January 1, 2026
Deepened: January 1, 2026 via /deepen-plan
Author: SAGE
Deadline: January 14, 2026 (Cronos Hackathon Phase 1)


Enhancement Summary (from /deepen-plan)

Review Agents Used: security-sentinel, kieran-python-reviewer, kieran-typescript-reviewer, architecture-strategist, data-integrity-guardian, best-practices-researcher

Key Improvements:

Priority Finding Fix
CRITICAL Chain detection bypassable via signature heuristics Use explicit chain field from payment network
CRITICAL Nonce consumed before verification complete Two-phase reserve/commit/release pattern
CRITICAL Unbounded local cache (memory exhaustion) Add TTL + size limit (10k max)
HIGH No nonce format validation Regex: ^[a-zA-Z0-9_-]{16,64}$
HIGH TypeScript uses unsafe unknown types Zod schemas with CAIP-2 validation
HIGH Wallet validation is Solana-only Chain-aware validation (EVM: 0x..., Solana: base58)
MEDIUM CDP Facilitator is SPOF Circuit breaker per chain
MEDIUM Bare exception handling Specific exception types

Executive Summary

Migrate the existing SAGE x402 DeFi API from x402 V1 to V2. The core API (12 exchange integrations, arbitrage analysis, caching, alerts) is production-ready and unchanged. This migration focuses solely on the payment verification layer.

Scope: ~50-100 lines of code changes
Risk: Low (core API untouched)
Outcome: Multi-chain x402 V2 API discoverable on Bazaar


1. Context & Background

1.1 What is x402?

x402 is an open protocol that operationalizes HTTP 402 "Payment Required" for machine-to-machine micropayments. Instead of API keys or subscriptions, agents pay per-request using stablecoins (USDC).

V1 Flow (current):

Request → 402 Response → Client signs payment → X-Payment header → Server verifies → Success

V2 Flow (target):

Request → 402 Response with PAYMENT-REQUIRED header → Client signs → PAYMENT-SIGNATURE header → Server verifies → Success

V2 Key Changes:

  1. New headers: PAYMENT-SIGNATURE and PAYMENT-REQUIRED (replaces X-Payment)
  2. Multi-chain via CAIP-2 standard (single endpoint, multiple chains)
  3. Plugin architecture (@x402/evm, @x402/svm)
  4. Reusable sessions (Sign-In-With-X) for reduced latency
  5. Bazaar discovery extension for agent discoverability

1.2 Why Migrate Now?

  1. V1 is deprecated - X-Payment header no longer works with V2 clients
  2. Solana dominance - 72% of x402 volume is on Solana (400ms finality vs 2s on Base)
  3. Cronos Hackathon - 300+ builders seeking DeFi APIs (ends Jan 23, 2026)
  4. CDP fees started Jan 1 - need proper facilitator integration

2. Current State Analysis

2.1 Architecture Overview

x402/
├── main.py                 # FastAPI app entry point
├── config.py               # Configuration (NETWORK, PRICE_PER_CALL, WALLET_ADDRESS)
├── utils.py                # create_402_response() - NEEDS V2 UPDATE
├── routers/
│   ├── funding_rates.py    # Main API endpoints - NEEDS V2 UPDATE
│   └── ...
├── services/
│   ├── exchange_service.py # 12 exchange integrations (unchanged)
│   ├── rate_limiter.py     # Wallet-based rate limiting (unchanged)
│   └── ...
└── packages/
    └── x402-payment/       # TypeScript payment module - NEEDS V2 UPDATE
        ├── package.json    # Currently: @coinbase/x402: ^0.7.1 (V1)
        └── src/index.ts    # Payment verification logic

2.2 What's Working (Don't Touch)

Component File Status
12 Exchange Integrations exchanges/*.py Production-ready
Arbitrage Analysis services/analysis_service.py Production-ready
Redis Caching services/cache_service.py Production-ready
Rate Limiting services/rate_limiter.py Production-ready

3. Target State

3.1 V2 Architecture

Client → PAYMENT-SIGNATURE header → FastAPI
                                      │
                              parse_payment()
                                      │
                           validate_wallet(chain)  ← NEW: chain-aware
                                      │
                     +------- chain routing -------+
                     │                             │
               EVMVerifier               SolanaVerifier
                     │                             │
         +--- circuit breaker ---+  +--- circuit breaker ---+  ← NEW
                     │                             │
              CDP Facilitator          CDP Facilitator
                     │                             │
         +----- nonce check (Redis, fail-closed) ----+  ← NEW: two-phase
                                      │
                            rate_limit (Redis/memory)
                                      │
                                API Response

3.2 V2 402 Response Schema

{
  "x402Version": "2",
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "maxAmountRequired": "10000",
      "resource": "/api/funding-rates",
      "payTo": "0xae3cDa2A4c50782868EAD3be4bFAedd96D07c17e",
      "token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
    },
    {
      "scheme": "exact",
      "network": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
      "maxAmountRequired": "10000",
      "resource": "/api/funding-rates",
      "payTo": "<solana-wallet-address>",
      "token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    }
  ]
}

4. Migration Steps (Updated with Research)

Step 1: Update TypeScript SDK

File: packages/x402-payment/package.json

{
  "dependencies": {
    "@x402/core": "^2.0.0",
    "@x402/evm": "^2.1.0",
    "@x402/svm": "^2.0.0",
    "@coinbase/x402": "^2.0.0",
    "zod": "^3.22.0"
  }
}

Research Finding: Add CAIP-2 type system:

type EVMChainId = 'eip155:1' | 'eip155:8453' | 'eip155:84532';
type SVMChainId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';
export type ChainId = EVMChainId | SVMChainId;

type ChainConfig =
  | { chainType: 'evm'; chainId: EVMChainId; wallet: `0x${string}` }
  | { chainType: 'svm'; chainId: SVMChainId; wallet: string };

Research Finding: Add Zod validation:

const PaymentPayloadSchema = z.object({
  x402Version: z.literal(2),
  network: z.string().regex(/^(eip155|solana):/),  // CAIP-2
  signature: z.string()
});

Step 2: Update Python Config

File: config.py

X402_VERSION = "2"
NETWORKS = {
    "base": "eip155:8453",
    "ethereum": "eip155:1",
    "solana": "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp"
}
USDC_ADDRESSES = {
    "base": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "solana": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
}

Step 3: Update 402 Response

File: utils.py

  • Change x402Version from "1" to "2"
  • Use CAIP-2 network identifiers
  • Add PAYMENT-REQUIRED header

Step 4: Update Payment Verification (CRITICAL FIXES)

File: routers/funding_rates.py

Research Finding - Chain Detection Fix:

def detect_payment_chain(payment_data: dict) -> str:
    """Use explicit chain from payment network, not signature format."""
    network = payment_data.get("network", "")
    chain = network.split(":")[0]
    if chain == "eip155":
        return "evm"
    elif chain == "solana":
        return "solana"
    raise ValueError(f"Unsupported chain: {chain}")

Research Finding - Error Handling Fix:

# Instead of bare except Exception:
except json.JSONDecodeError as e:
    return False, None, f"Invalid payment signature: {e}"
except PaymentVerificationError as e:
    return False, None, f"Payment verification failed: {e}"
except (ConnectionError, TimeoutError) as e:
    return False, None, f"Network error: {e}"

Step 5: Add Nonce Service (CRITICAL - NEW)

File: services/nonce_service.py

Research Finding - Two-Phase Pattern:

class NonceService:
    NONCE_TTL = 86400
    MAX_LOCAL_CACHE = 10000
    NONCE_PATTERN = re.compile(r'^[a-zA-Z0-9_-]{16,64}$')

    async def reserve_nonce(self, nonce: str, ttl: int = 60) -> bool:
        """Reserve nonce temporarily during verification."""
        if not self.NONCE_PATTERN.match(nonce):
            raise ValueError("Invalid nonce format")
        key = f"nonce:pending:{nonce}"
        return await self.redis.set(key, "1", nx=True, ex=ttl) is not None

    async def commit_nonce(self, nonce: str) -> bool:
        """Permanently consume after successful verification."""
        pipe = self.redis.pipeline()
        pipe.delete(f"nonce:pending:{nonce}")
        pipe.set(f"nonce:{nonce}", str(int(time.time())), nx=True, ex=self.NONCE_TTL)
        results = await pipe.execute()
        return results[1] is not None

    async def release_nonce(self, nonce: str):
        """Release on verification failure."""
        await self.redis.delete(f"nonce:pending:{nonce}")

Research Finding - Fail Closed:

if not self.redis:
    raise NonceServiceUnavailable("Redis required for replay protection")

Step 6: Add Bazaar Metadata Endpoint

File: routers/discovery.py (NEW)

@router.get("/.well-known/x402")
async def get_x402_metadata():
    return {
        "x402Version": "2",
        "name": "sage-defi-oracle",
        "endpoints": [...],
        "networks": [...]
    }

5. Critical Research Findings (Detailed)

Security Review (security-sentinel)

  1. Chain detection is bypassable - Any JSON with "signature" key routes to EVM, any base64 to Solana. Fix: use explicit network field.

  2. Local nonce cache has no TTL - Memory exhaustion DoS + replay after restart. Fix: OrderedDict with max size + TTL.

  3. Nonce format not validated - Empty/malformed nonces pass through. Fix: regex validation.

Data Integrity Review (data-integrity-guardian)

  1. Nonce consumed before verification - If verification fails, nonce is burned, user can't retry. Fix: two-phase reserve/commit/release.

  2. In-memory fallback is risky - Replay attacks possible across instances. Fix: fail closed for nonces.

TypeScript Review (kieran-typescript-reviewer)

  1. Type-unsafe unknown with forced assertions - Runtime crashes possible. Fix: Zod schemas.

  2. Missing CAIP-2 type system - Invalid chains pass compile-time checks. Fix: discriminated unions.

  3. Wallet validation is Solana-only - EVM wallets fail. Fix: chain-aware validation.

Architecture Review (architecture-strategist)

  1. CDP Facilitator is SPOF - Both chains route through single point. Fix: circuit breaker per chain.

  2. Error boundaries undefined - Chain-specific errors not normalized. Fix: error taxonomy.


6. Success Criteria

  • 402 response returns V2 schema with CAIP-2 networks
  • Chain detection uses explicit field, not signature heuristics
  • Nonce uses two-phase reserve/commit/release pattern
  • Nonce service fails closed without Redis
  • Local cache bounded with TTL
  • TypeScript uses CAIP-2 types with Zod validation
  • Wallet validation is chain-aware
  • Circuit breakers per chain
  • Exception handling is specific, not bare
  • All existing tests pass
  • Listed on x402 Bazaar

7. Design Decisions (Confirmed)

  1. Solana via CDP Facilitator: ✅ Confirmed available. CDP supports both Base and Solana with fee-free USDC payments.
  2. EVM chains beyond Base: ❌ No. Base only for Phase 1. Rationale: Base is 28% of volume, Solana is 72%. Other EVM chains have minimal x402 volume. Can add later if demand exists.
  3. Traffic ratio: ~30% EVM (Base) / ~70% Solana. Solana flipped Base in Dec 2025 due to 400ms finality. Most agent builders are on Solana now.

8. Reference Links

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions