{
  "$comment": "GENERATED FROM api/_tools/<slug>/route.mjs — do not edit by hand",
  "type": "https://eips.ethereum.org/EIPS/eip-8257#tool-manifest-v1",
  "name": "erc20-holder-overlap",
  "description": "Computes holder overlap between two ERC-20 tokens on Base. Returns the number of wallets holding both tokens (above optional minimum balances), overlap percentage, Jaccard similarity index, and top-25 shared wallets by combined holdings. Useful for community analysis, airdrop targeting, and token ecosystem research.",
  "endpoint": "https://www.clawbots.org/api/tools/erc20-holder-overlap",
  "inputs": {
    "type": "object",
    "properties": {
      "tokenA": {
        "type": "string",
        "description": "First ERC-20 token contract address on Base. E.g. '0x4200000000000000000000000000000000000006' for WETH.",
        "example": "0x4200000000000000000000000000000000000006"
      },
      "tokenB": {
        "type": "string",
        "description": "Second ERC-20 token contract address to compare against. Must differ from tokenA.",
        "example": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
      },
      "minBalanceA": {
        "type": "string",
        "description": "Minimum token balance for tokenA holders in atomic units (wei). Default '0' includes all holders. E.g. '1000000' for 1 USDC (6 decimals).",
        "default": "0"
      },
      "minBalanceB": {
        "type": "string",
        "description": "Minimum token balance for tokenB holders in atomic units (wei). Default '0' includes all holders.",
        "default": "0"
      },
      "maxPages": {
        "type": "integer",
        "description": "Max Blockscout pages to walk per token (default 5, max 20). Higher = more holders scanned but slower.",
        "default": 5
      }
    },
    "required": [
      "tokenA",
      "tokenB"
    ],
    "additionalProperties": false
  },
  "outputs": {
    "type": "object",
    "properties": {
      "tokenA": {
        "type": "string",
        "description": "First token address (echoed, lowercased)"
      },
      "tokenB": {
        "type": "string",
        "description": "Second token address (echoed, lowercased)"
      },
      "holdersA": {
        "type": "integer",
        "description": "Distinct holders of tokenA above minBalanceA (within scanned pages)"
      },
      "holdersB": {
        "type": "integer",
        "description": "Distinct holders of tokenB above minBalanceB (within scanned pages)"
      },
      "minBalanceA": {
        "type": "string",
        "description": "Minimum balance filter applied for tokenA (atomic units)"
      },
      "minBalanceB": {
        "type": "string",
        "description": "Minimum balance filter applied for tokenB (atomic units)"
      },
      "overlapCount": {
        "type": "integer",
        "description": "Wallets holding both tokens above their respective minimums"
      },
      "overlapPct": {
        "type": "number",
        "description": "overlapCount / min(holdersA, holdersB) * 100"
      },
      "jaccardIndex": {
        "type": "number",
        "description": "|A ∩ B| / |A ∪ B|, range 0-1"
      },
      "topOverlapWallets": {
        "type": "array",
        "description": "Up to 25 shared wallets, sorted by combined balance desc",
        "items": {
          "type": "object",
          "properties": {
            "address": {
              "type": "string"
            },
            "balanceA": {
              "type": "string"
            },
            "balanceB": {
              "type": "string"
            }
          },
          "required": [
            "address",
            "balanceA",
            "balanceB"
          ]
        }
      },
      "pagesScannedA": {
        "type": "integer",
        "description": "Pages walked for tokenA"
      },
      "pagesScannedB": {
        "type": "integer",
        "description": "Pages walked for tokenB"
      },
      "note": {
        "type": [
          "string",
          "null"
        ],
        "description": "Warning when maxPages limit was hit"
      },
      "fetchedAt": {
        "type": "string",
        "format": "date-time"
      }
    },
    "required": [
      "tokenA",
      "tokenB",
      "holdersA",
      "holdersB",
      "overlapCount",
      "overlapPct",
      "jaccardIndex",
      "topOverlapWallets",
      "pagesScannedA",
      "pagesScannedB",
      "fetchedAt"
    ]
  },
  "creatorAddress": "0xef2cc7d15d3421629f93ffa39727f14179f31c75",
  "tags": [
    "erc20",
    "token",
    "holders",
    "overlap",
    "analysis",
    "airdrop",
    "base"
  ],
  "pricing": [
    {
      "amount": "0.05",
      "asset": "USDC",
      "recipient": "0xef2cc7d15d3421629f93ffa39727f14179f31c75",
      "protocol": "x402"
    }
  ],
  "authentication": [
    {
      "type": "siwe",
      "description": "Sign In With Ethereum. Holders of the Axiom Tool Pass NFT get free unlimited access. Sign a SIWE message proving wallet ownership; the server verifies your wallet holds the pass on-chain.",
      "header": "Authorization: SIWE <base64url(message)>.<0xsignature>",
      "passContract": "0xfc9ce3990f85fA1A3a0eE51a710642396a6Cad82",
      "chain": "base",
      "chainId": 8453,
      "collection": "https://opensea.io/collection/axiom-tool-pass"
    },
    {
      "type": "x402",
      "description": "Pay per call in USDC on Base via the x402 protocol. No account or API key needed.",
      "header": "x-payment: <x402 authorization>"
    }
  ],
  "onChain": {
    "registry": "0x265BB2DBFC0A8165C9A1941Eb1372F349baD2cf1",
    "chainId": 8453,
    "toolId": 63
  }
}
