[FEATURE] x402-payable external data sources (pay per call with the existing wallet, no API keys)
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.
{
"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 whenpaymentis 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
SafeHTTPClientas 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 directionkernel → mcp/paymentmirrors whatprovider/nofxosalready does — no cycle. - Cap check: parse
accepts[0].amount(atomic USDC units ÷ 1e6) againstmax_usd_per_callinside the sign func. - Schema: two fields on
store.ExternalDataSource+ mirrored optional fields inweb/src/types/strategy.tsso Strategy Studio round-trips config. No UI form changes in v1. - Zero new dependencies.
- Estimated diff < 300 lines including tests.
✅ Acceptance Criteria
-
ExternalDataSourceacceptspayment: "x402"+max_usd_per_call; unsetpaymentkeeps 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.gopatterns) - 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 buildall 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.).
Source: NoFxAiOS/nofx