#1532·nofx

[FEATURE] x402-payable external data sources (pay per call with the existing wallet, no API keys)

Author: SELAT-DEVCreated Jul 29, 2026Updated Aug 12, 2026

Feature Description

Let an external_data_sources entry opt into x402 payment, so strategies can consume pay-per-call data APIs with the same Base wallet that already pays for AI inference — no API keys, and a hard per-call USD spend cap the user controls.

json
{
  "name": "smart_money_flow",
  "type": "api",
  "url": "https://api.example.com/v1/flows/BTC",
  "method": "GET",
  "payment": "x402",
  "max_usd_per_call": 0.05
}

Problem to Solve

NOFX already speaks x402 end-to-end — mcp/payment/x402.go handles the full 402 → EIP-712 sign → retry flow for Claw402 inference, and the wallet key is already plumbed into the strategy engine (NewStrategyEngine(config, claw402WalletKey...)).

But external data sources (store.ExternalDataSource) only support static headers. That means:

  • A 402 response from a data API today is just a failed fetch — the machinery to pay for it sits one package away, unused.
  • Every data source still needs an API key: sign up, manage secrets, put them in config. This is exactly the friction the wallet was supposed to remove ("a wallet on Base replaces every API key" — currently only true for LLM calls).
  • A growing set of trading-relevant APIs now sell per-call over x402 with no signup (on-chain analytics, smart-money flows, swap quotes, stock/macro indicators, sentiment). NOFX traders can't reach any of them without waiting for a dedicated provider integration. Sellers are already showing up in this tracker offering exactly this (#1432 — x402 trading signals on Base) with no way for NOFX agents to consume them.

This also directly serves two open roadmap items: "Market sentiment aggregator — combine multiple data sources for enhanced AI decision making" and "Custom indicator API" (roadmap §1.3) — both need data sources, and x402 sources need zero key management.

Proposed Solution

Two new optional fields on ExternalDataSource:

  • payment: "x402" — strictly opt-in per source; default (unset) never pays, current behavior unchanged.
  • max_usd_per_call — required when payment is set. The offered price is checked before signing; anything above the cap is refused with a clear error. Never pay silently, never pay unbounded.

Flow: fetchSingleExternalSource gets a 402 → parse Payment-Required → enforce cap → sign with the engine's existing wallet key → retry via the existing payment.DoX402Request() machinery → JSON lands in the AI context like any other external source. Paid amount + tx hash logged per call, tagged with the source name.

Safety properties:

  • Opt-in only — no existing config changes behavior.
  • Hard per-call cap enforced client-side before signing (x402 guarantees the server can only settle what was signed).
  • Same SSRF validation and SafeHTTPClient as unpaid sources, including on the paid retry.
  • Base-only in v1 (eip155:8453) — matches where users fund the wallet; refuse offers on other networks rather than sign chains the user hasn't funded.

Technical Details

  • Reuses payment.DoX402Request() / SignX402Payment() unchanged; import direction kernel → mcp/payment mirrors what provider/nofxos already does — no cycle.
  • Cap check: parse accepts[0].amount (atomic USDC units ÷ 1e6) against max_usd_per_call inside the sign func.
  • Schema: two fields on store.ExternalDataSource + mirrored optional fields in web/src/types/strategy.ts so Strategy Studio round-trips config. No UI form changes in v1.
  • Zero new dependencies.
  • Estimated diff < 300 lines including tests.

✅ Acceptance Criteria

  • ExternalDataSource accepts payment: "x402" + max_usd_per_call; unset payment keeps exact current behavior (regression-tested)
  • 402 from an opted-in source is paid and the JSON reaches the AI context (httptest-based unit test, mirroring mcp/payment/x402_test.go patterns)
  • Offered price above the cap → refused before signing, no payment attempted, clear error in trader logs
  • Non-Base network offer → refused
  • Paid amount + tx hash logged per call with source name
  • SSRF validation applies to the paid path
  • go build, go test ./..., go fmt, go vet, npm run build all clean

Additional Context

  • Happy to implement this — I have the change scoped to the above and can have a PR up shortly after any design feedback here (branch off dev, conventional title, CLA signed).
  • Natural follow-ups I'd keep out of v1 to stay small: a daily aggregate cap (max_usd_per_day), multi-chain support, and a Strategy Studio form field for the new options.
  • Example x402-payable data APIs traders could plug in today: Nansen (smart-money/wallet data), CoinGecko and 0x (quotes, $0.01/call), The Graph (on-chain queries), the signals offered in #1432, plus stock/macro indicator endpoints — discoverable via the public x402 catalogs (Coinbase Bazaar, x402scan, SELAT, etc.).