{
  "openapi": "3.1.0",
  "info": {
    "title": "Ulvane API",
    "version": "1.1.0",
    "summary": "The data layer AI trading agents trust before they trade.",
    "description": "Onchain intelligence for Base, priced per call via the x402 protocol (HTTP 402 Payment Required). No API keys, no accounts, no subscriptions: request an endpoint, receive a 402 with payment requirements, pay in USDC on Base mainnet, get your answer back. Every paid response is signed (EIP-191) so it can be verified offline without asking Ulvane again. See https://www.ulvane.xyz/docs for full documentation and https://www.ulvane.xyz/llms.txt for an agent-oriented summary.",
    "contact": { "url": "https://x.com/ulvaneapp" }
  },
  "servers": [{ "url": "https://www.ulvane.xyz" }],
  "paths": {
    "/api/token/{address}/risk": {
      "get": {
        "operationId": "getTokenRisk",
        "summary": "Token risk / impersonator check",
        "description": "Checks whether a token address is a hand-verified real token, a symbol impersonator (scam clone), or simply not yet vetted.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
            "description": "Token contract address on Base."
          }
        ],
        "responses": {
          "200": {
            "description": "Signed token risk assessment.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TokenRiskResponse" } } }
          },
          "402": {
            "description": "Payment required. See the Payment-Required header and body for x402 payment details.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } }
          },
          "502": {
            "description": "Upstream data source unavailable.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          }
        },
        "x-price": "$0.01",
        "x-x402-network": "eip155:8453"
      }
    },
    "/api/pnl/{wallet}": {
      "get": {
        "operationId": "getWalletPnl",
        "summary": "Wallet realized/unrealized PnL",
        "description": "A wallet's profit on Base, computed with a running-average cost basis across its swap history — not just current balance times price.",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
            "description": "Wallet address on Base."
          }
        ],
        "responses": {
          "200": {
            "description": "Signed wallet PnL breakdown.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletPnlResponse" } } }
          },
          "402": {
            "description": "Payment required. See the Payment-Required header and body for x402 payment details.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } }
          },
          "502": {
            "description": "Upstream data source unavailable.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          }
        },
        "x-price": "$0.02",
        "x-x402-network": "eip155:8453"
      }
    },
    "/api/token/{address}/top-traders": {
      "get": {
        "operationId": "getTopTraders",
        "summary": "Top traders / whale tracking for a token",
        "description": "Who is actually trading a token on Base, ranked by total volume moved (not by current balance), with a per-trader running-average cost basis.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
            "description": "Token contract address on Base."
          }
        ],
        "responses": {
          "200": {
            "description": "Signed top-traders breakdown.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TopTradersResponse" } } }
          },
          "402": {
            "description": "Payment required. See the Payment-Required header and body for x402 payment details.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } }
          },
          "502": {
            "description": "Upstream data source unavailable.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          }
        },
        "x-price": "$0.03",
        "x-x402-network": "eip155:8453"
      }
    },
    "/api/wallet/{wallet}/risk": {
      "get": {
        "operationId": "getWalletRisk",
        "summary": "Wallet risk / reputation check",
        "description": "The wallet-level counterpart to the token risk check: flags a wallet if it has recently traded any token on Ulvane's impersonator allowlist.",
        "parameters": [
          {
            "name": "wallet",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^0x[a-fA-F0-9]{40}$" },
            "description": "Wallet address on Base."
          }
        ],
        "responses": {
          "200": {
            "description": "Signed wallet risk assessment.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WalletRiskResponse" } } }
          },
          "402": {
            "description": "Payment required. See the Payment-Required header and body for x402 payment details.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PaymentRequired" } } }
          },
          "502": {
            "description": "Upstream data source unavailable.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorResponse" } } }
          }
        },
        "x-price": "$0.02",
        "x-x402-network": "eip155:8453"
      }
    }
  },
  "components": {
    "schemas": {
      "SignatureFields": {
        "type": "object",
        "properties": {
          "signature": {
            "type": ["string", "null"],
            "description": "EIP-191 personal-sign signature over the JSON-stringified response body with signature/signerAddress omitted. Null only if the server's signing key is unavailable."
          },
          "signerAddress": {
            "type": ["string", "null"],
            "description": "The address that produced `signature`. Verify with viem's verifyMessage — see https://www.ulvane.xyz/docs#verifiable-responses."
          }
        }
      },
      "TokenRiskResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/SignatureFields" },
          {
            "type": "object",
            "properties": {
              "address": { "type": "string" },
              "symbol": { "type": "string" },
              "name": { "type": "string" },
              "status": { "type": "string", "enum": ["verified", "impersonator", "unverified"] },
              "note": { "type": "string" },
              "impersonatedSymbol": { "type": ["string", "null"] },
              "asOf": { "type": "string", "format": "date-time" }
            },
            "required": ["address", "symbol", "name", "status", "note", "asOf"]
          }
        ]
      },
      "PnlPosition": {
        "type": "object",
        "properties": {
          "tokenAddress": { "type": "string" },
          "symbol": { "type": "string" },
          "avgCostUsd": { "type": "number" },
          "amountHeld": { "type": "number" },
          "unrealizedUsd": { "type": "number" }
        }
      },
      "PnlPoint": {
        "type": "object",
        "properties": {
          "time": { "type": "integer", "description": "Unix timestamp (seconds)." },
          "realizedUsd": { "type": "number" }
        }
      },
      "WalletPnlResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/SignatureFields" },
          {
            "type": "object",
            "properties": {
              "wallet": { "type": "string" },
              "realizedUsd": { "type": "number" },
              "unrealizedUsd": { "type": "number" },
              "wins": { "type": "integer" },
              "losses": { "type": "integer" },
              "totalTrades": { "type": "integer" },
              "points": { "type": "array", "items": { "$ref": "#/components/schemas/PnlPoint" } },
              "positions": { "type": "array", "items": { "$ref": "#/components/schemas/PnlPosition" } },
              "windowNote": { "type": "string" },
              "asOf": { "type": "string", "format": "date-time" }
            },
            "required": ["wallet", "realizedUsd", "unrealizedUsd", "totalTrades", "asOf"]
          }
        ]
      },
      "TraderStat": {
        "type": "object",
        "properties": {
          "trader": { "type": "string" },
          "boughtUsd": { "type": "number" },
          "soldUsd": { "type": "number" },
          "boughtAmount": { "type": "number" },
          "soldAmount": { "type": "number" },
          "remainingAmount": { "type": "number" },
          "realizedUsd": { "type": "number" },
          "unrealizedUsd": { "type": "number" },
          "totalPnlUsd": { "type": "number" },
          "tradeCount": { "type": "integer" },
          "lastTradeTime": { "type": "integer", "description": "Unix timestamp (seconds)." }
        }
      },
      "TopTradersResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/SignatureFields" },
          {
            "type": "object",
            "properties": {
              "tokenAddress": { "type": "string" },
              "currentPriceUsd": { "type": "number" },
              "traders": { "type": "array", "items": { "$ref": "#/components/schemas/TraderStat" } },
              "windowNote": { "type": "string" },
              "asOf": { "type": "string", "format": "date-time" }
            },
            "required": ["tokenAddress", "currentPriceUsd", "traders", "asOf"]
          }
        ]
      },
      "PaymentRequired": {
        "type": "object",
        "description": "The x402 'exact' scheme payment-required body, also returned base64-encoded in the Payment-Required response header.",
        "properties": {
          "x402Version": { "type": "integer" },
          "error": { "type": "string" },
          "resource": {
            "type": "object",
            "properties": {
              "url": { "type": "string" },
              "description": { "type": "string" },
              "mimeType": { "type": "string" }
            }
          },
          "accepts": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scheme": { "type": "string", "const": "exact" },
                "network": { "type": "string", "description": "CAIP-2 chain id. Base mainnet is eip155:8453.", "const": "eip155:8453" },
                "amount": { "type": "string", "description": "Price in the asset's smallest unit (USDC has 6 decimals)." },
                "asset": { "type": "string", "description": "USDC contract address on Base." },
                "payTo": { "type": "string" },
                "maxTimeoutSeconds": { "type": "integer" },
                "extra": { "type": "object" }
              }
            }
          }
        }
      },
      "FlaggedToken": {
        "type": "object",
        "properties": {
          "address": { "type": "string" },
          "symbol": { "type": "string" }
        }
      },
      "WalletRiskResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/SignatureFields" },
          {
            "type": "object",
            "properties": {
              "wallet": { "type": "string" },
              "tradesAnalyzed": { "type": "integer" },
              "distinctTokensTraded": { "type": "integer" },
              "flaggedTokens": { "type": "array", "items": { "$ref": "#/components/schemas/FlaggedToken" } },
              "status": { "type": "string", "enum": ["flagged", "clean", "insufficient_data"] },
              "note": { "type": "string" },
              "windowNote": { "type": "string" },
              "asOf": { "type": "string", "format": "date-time" }
            },
            "required": ["wallet", "tradesAnalyzed", "distinctTokensTraded", "status", "note", "asOf"]
          }
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "properties": { "error": { "type": "string" } }
      }
    }
  }
}
