YieldSignal

Real-time, risk-weighted USDC and WETH lending APY across Aave, Compound, Morpho, Moonwell, Euler and Fluid on Base โ paid per call via the x402 protocol. $0.01 USDC, no API key, no signup. First 3 calls/day per IP are free via ?trial=1.
Live: https://yieldsignal.vercel.app
GET https://yieldsignal.vercel.app/signal/usdc-base-yield
GET https://yieldsignal.vercel.app/signal/weth-base-yield
Call it without payment first and you'll get a 402 Payment Required with the exact price/asset/network to pay. Any x402-compatible client can complete the payment automatically โ for example with @x402/fetch:
import { wrapFetchWithPayment } from "@x402/fetch";
import { CdpX402Client } from "@coinbase/cdp-sdk/x402";
const client = new CdpX402Client();
const fetchWithPayment = wrapFetchWithPayment(fetch, client);
const res = await fetchWithPayment("https://yieldsignal.vercel.app/signal/usdc-base-yield");
console.log(await res.json());
MCP
Also available as a paid MCP tool at https://yieldsignal.vercel.app/mcp (get_yield_signal, optional asset: "USDC" or "WETH", defaults to USDC) โ most autonomous-agent frameworks discover/call tools via MCP rather than hand-rolled x402 HTTP clients. Uses the official @x402/mcp package; payment is gated per tool call (tools/list/initialize stay free, only get_yield_signal requires payment).
Response shape
Every rate is tagged with where it came from โ onchain/api (Aave, Compound, Morpho โ read directly from the protocol) or defillama (Moonwell, Euler, Fluid โ via the DefiLlama yields API). No estimated or fabricated numbers: a source that fails or returns invalid data is omitted from the response, never guessed at.
{
"asset": "USDC",
"bestProtocol": "compound",
"gapBps": 57,
"rates": [
{ "protocol": "compound", "apyBps": 490, "weightedApyBps": 485, "source": "onchain", "asOf": "2026-07-17T..." },
{ "protocol": "aave", "apyBps": 307, "weightedApyBps": 307, "source": "onchain", "asOf": "..." }
],
"asOf": "2026-07-17T..."
}
Verifiability
Two independent ways to check a response wasn't tampered with or fabricated, neither requiring you to trust this server's uptime at the moment you check:
- Signed responses โ every REST/MCP response is signed (EIP-191
personal_sign) by the same payTo address the 402 payment requirement names. REST: X-Signal-Signature/X-Signal-Signer headers over the exact response body. MCP: a sibling content block over the exact preceding block's text. Verify with viem's verifyMessage.
- On-chain attestations (EAS, Base mainnet) โ periodic, public, permanent records of "at time T, protocol X paid Y bps, Z ahead of the runner-up," independently checkable on easscan.org without trusting this server at all. Same attester address as the signed responses above. See
src/attestation/, src/cli/registerSchema.ts and src/cli/attestSignal.ts โ attestation is a manual, deliberately-triggered action (real gas cost each time), not an automatic background job.
Architecture
Sibling project to YieldPilot (a personal Aave/Morpho/Compound rebalancer), but fully separate: own CDP credentials, own receiver wallet, no shared runtime code. See CLAUDE.md and SECURITY.md for the full technical writeup and threat model.
src/expressApp.ts โ the Express app + x402 payment gate (createX402Server, @x402/express), reused by both the local dev server (src/server.ts) and the Vercel serverless entrypoint (api/index.ts). Registers one payment-protected route per asset (RESOURCE_PATHS) plus an unpaid /health liveness check.
src/signal/ โ the pure, deterministic comparison logic (no I/O, fully unit tested).
src/market-data/ โ the two-layer data sourcing (direct reads + DefiLlama), parametrized by asset (AssetId, USDC/WETH) via config/networks.ts's BASE_ASSETS.
src/wallet/walletLock.ts โ pins the receiver wallet address (via EXPECTED_WALLET_ADDRESS in production, since serverless has no persistent disk) so a CDP credential rotation is caught loudly instead of silently redirecting payments.
src/mcp.ts โ the get_yield_signal MCP tool (optional asset param), gated per-call with @x402/mcp's createPaymentWrapper (not the whole-route Express middleware, which would paywall tools/list/initialize too).
src/market-data/cache.ts โ 30s TTL on every rate reader (direct + DefiLlama), one cache instance per asset, so a burst of concurrent paid calls doesn't hammer public RPC/API endpoints.
src/freeTrial.ts โ 3 free calls/day per IP (in-memory, best-effort โ not a hard cap across serverless instances, just adoption-friction removal). Only applied on ?trial=1; a bare GET always 402s so x402 discovery crawlers (x402scan, Bazaar, trust indexes) correctly detect this as a paid endpoint.
src/notify/paymentLog.ts โ logs payer/tx/network/amount for every settled payment (onAfterSettle, both the REST and MCP payment servers), plus a usage line per call (paid or free) โ revenue/usage visibility, not just a live product.
src/cli/withdraw.ts โ sweeps accumulated USDC to the owner's personal wallet, manual CONFIRM required, never automatic.
src/wallet/signerAccount.ts โ resolves the same receiver wallet with signMessage/sendTransaction exposed (createX402Server only exposes the address); used for response signing and EAS attestation.
src/attestation/ โ EAS schema definition and calldata encoding (pure, unit tested) for the on-chain attestation described above.
src/cli/registerSchema.ts / src/cli/attestSignal.ts โ one-time schema registration and manual per-attestation publishing, same CONFIRM pattern as withdraw.ts.
Local development
npm install
npm test
npm run signal
npm run dev
npm run test:paid
npm run withdraw
npm run register-schema
npm run attest
See .env.example for the required variables. Generate your own dedicated CDP project/credentials at portal.cdp.coinbase.com โ never reuse another project's.
Source
github.com/Stakemate369/yieldsignal โ open source, CI runs typecheck + full test suite on every push/PR.
Why Spark, Seamless and Silo aren't in the protocol list
Checked against yields.llama.fi/pools on 2026-07-16, filtering chain=Base + symbol=USDC: Spark only has a USDS pool (not USDC) on Base; Seamless and Silo have no indexed Base pool at all right now. Rather than fabricate a number, these three are left out until a real USDC market exists for them on Base โ see the comment in src/market-data/types.ts.
Why WETH but not WBTC/cbBTC
Checked live against yields.llama.fi/pools on 2026-07-17: there's no canonical "WBTC" market on Base, only Coinbase's cbBTC (a different asset), and where it does have a market its supply APY sits at ~0-0.2% across all six protocols โ a signal too flat to be worth selling ("which is best" barely matters when everyone's tied near zero). WETH, by contrast, has real, distinct yield across all six protocols (0.66%-3.7%), so it's the asset that got added instead.